Comme le dit la documentation officielle de KO ( La syntaxe de liaison de données ), une liaison a un nom et une valeur :
<div data-bind='foreach: { data: metaProps }'> <span data-bind="binding: $parent[name]"> </div>
¿Peut-on définir la partie nom dynamiquement avec une variable?
eval
ne fonctionne pas ici.
Contexte: J'ai un tas de propriétés qui peuvent être du texte normal, une URL, un numéro de téléphone, un isbn, etc. J'ai aussi des liaisons personnalisées pour certaines d'entre elles, donc je dois appliquer pour chacune la liaison qui convient le mieux ... par programme .
//HTML <div data-bind='foreach: { data: metaProps }'> <!-- ko if: binding == 'url' --> <span data-bind="url: $parent[name]"> <!-- /ko --> <!-- ko if: binding == 'isbn' --> <span data-bind="isbn: $parent[name]"> <!-- /ko --> <!-- ko if: binding == 'text' --> <span data-bind="text: $parent[name]"> <!-- /ko --> ... </div>
La clé ici est que les accessoires de mon viewmodel dépendent du sous-type spécifique de l'objet modèle, donc je ne veux pas implémenter un modèle HTML personnalisé pour chaque sous-type (aussi de nombreux). À l'heure actuelle, mon modèle ressemble à:
//viewmodel props this.url1 = ko.observable(...); this.text1 = ko.observable(...); this.isbn1 = ko.observable(...); ... var metaProps = [ { name: 'url1', binding: 'url' }, { name: 'text1', binding: 'text' }, { name: 'isbn1', binding: 'isbn' }, ... ]
Mon objectif est de simplifier le HTML en quelque chose comme:
<span data-bind="name: value"></span>
3 Réponses :
Je ne sais pas si cela peut résoudre votre problème complet, mais vous pouvez générer du HTML dynamiquement.
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div data-bind="foreach:metaProps"> <span data-bind="html:val"><br/> </div>
function VM(){ var self = this; self.metaProps = [ { name: 'url1', val: '<a href="https://google.com">url</a>' }, { name: 'text1', val: 'text' }, { name: 'isbn1', val: 'isbn' }, ] } ko.applyBindings(new VM());
Il existe une fonction utilitaire (malheureusement non documentée) applyBindingsToNode
qui vous permettrait de créer une liaison personnalisée qui ferait probablement ce que vous voulez:
<div data-bind="foreach: { data: metaProps, as: 'metaProp' }"> <!-- ko component: { name: metaProp.component, params: { content: metaProp.content } } --> <!-- /ko --> </div>
Et appelez ça ressemble à ceci:
var metaProps = [ { content: this.url1, component: 'url' }, { content: this.text1, component: 'text' }, { content: this.isbn1, component: 'isbn' }, ... ]
J'aime l'idée des composants, comme suggéré par @Tomalak, car elle permet une plus grande flexibilité dans le balisage . Un petit exemple pour démontrer:
Pour enregistrer les composants:
ko.components.register('isbn', { viewModel: function(params) { this.content = params.content; }, template: '<span data-bind="text: content"></span>' }); ko.components.register('url', { viewModel: function(params) { this.content = params.content; }, template: '<a data-bind="attr: { href: content }, text: content"></a>' });
Modifiez un peu votre objet metaProps pour que le nom soit plus clair:
<div data-bind="foreach: { data: metaProps, as: 'metaProp' }"> <span data-bind="dynamicBinding: metaProp"></span> </div>
Et puis appelez la liaison component
pour charger les composants.
ko.bindingHandlers.dynamicBinding = { update: function(element, valueAccessor, allBindings, viewModel, bindingContext) { var bindings = {}; var bindingData = valueAccessor(); bindings[bindingData['binding']] = bindingContext.$data[bindingData['name']]; ko.applyBindingsToNode(element, bindings, bindingContext); } };
Je travaillerais avec des modèles , car:
pour tous vos éléments, chaque modèle peut être différent. isbn
de manière cohérente n'importe où dans votre vue. Cela pourrait même remplacer entièrement le besoin d'un gestionnaire de liaison personnalisé pour les ISBN.
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script> <div data-bind="foreach: metaProps"> <div data-bind="template: 'template-' + type"></div> </div> <script type="text/html" id="template-text"> <span data-bind="text: value"></span> </script> <script type="text/html" id="template-isbn"> <span class="isbn" data-bind="isbn: value"></span> </script> <script type="text/html" id="template-url"> <a data-bind="attr: {href: value}, text: text"></a> </script>
.isbn {font-family: monospace;}
// as a mockup of your real "isbn" handler let's just use "text" ko.bindingHandlers.isbn = ko.bindingHandlers.text; ko.applyBindings({ metaProps: [ {type: 'text', value: 'Some sample text'}, {type: 'isbn', value: '978-1491914311'}, {type: 'url', value: 'https://knockoutjs.com/documentation/introduction.html', text: 'Knockout Docs'} ] });
Si vous aimez mieux les composants , c'est une alternative.