Comment est-il possible de faire fonctionner la liaison de données knockout sur des éléments générés dynamiquement? Par exemple, j'insère un simple menu de sélection html dans un div et je souhaite remplir les options à l'aide de la liaison des options à élimination directe. Voici à quoi ressemble mon code:
$('#menu').html('<select name="list" data-bind="options: listItems"></select>');
mais cette méthode ne fonctionne pas. Des idées?
Si vous ajoutez cet élément à la volée après avoir lié votre modèle de vue, il ne sera pas dans le modèle de vue et ne sera pas mis à jour. Vous pouvez faire deux choses.
ko.applyBindings();
Knockout 3.3
ko.bindingHandlers.htmlWithBinding = {
'init': function() {
return { 'controlsDescendantBindings': true };
},
'update': function (element, valueAccessor, allBindings, viewModel, bindingContext) {
element.innerHTML = valueAccessor();
ko.applyBindingsToDescendants(bindingContext, element);
}
};
L'extrait de code ci-dessus vous permet d'injecter des éléments html dynamiquement avec la propriété "htmlWithBinding". Les éléments enfants qui sont ajoutés sont ensuite également évalués ... c'est-à-dire leurs attributs de liaison de données.
réécrire le code de liaison html ou en créer un nouveau. Parce que la liaison html empêche les "liaisons injectées" en html dynamique:
ko.bindingHandlers['html'] = {
//'init': function() {
// return { 'controlsDescendantBindings': true }; // this line prevents parse "injected binding"
//},
'update': function (element, valueAccessor) {
// setHtml will unwrap the value if needed
ko.utils.setHtml(element, valueAccessor());
}
};
Pour v3.4.0, utilisez la liaison personnalisée ci-dessous:
ko.bindingHandlers['dynamicHtml'] = {
'update': function (element, valueAccessor, allBindings, viewModel, bindingContext) {
// setHtml will unwrap the value if needed
ko.utils.setHtml(element, valueAccessor());
ko.applyBindingsToDescendants(bindingContext, element);
}
};
EDIT: Il semble que cela ne fonctionne pas depuis la version 2.3 IIRC comme indiqué par LosManos
Vous pouvez ajouter un autre observable à votre modèle de vue à l'aide de myViewModel [newObservable] = ko.observable ('')
Après cela, appelez à nouveau à ko.applyBindings.
Voici une page simple où j'ajoute des paragraphes dynamiquement et le nouveau modèle de vue et les liaisons fonctionnent parfaitement.
// myViewModel starts only with one observable
var myViewModel = {
paragraph0: ko.observable('First')
};
var count = 0;
$(document).ready(function() {
ko.applyBindings(myViewModel);
$('#add').click(function() {
// Add a new paragraph and make the binding
addParagraph();
// Re-apply!
ko.applyBindings(myViewModel);
return false;
});
});
function addParagraph() {
count++;
var newObservableName = 'paragraph' + count;
$('<p data-bind="text: ' + newObservableName + '"></p>').appendTo('#placeholder');
// Here is where the magic happens
myViewModel[newObservableName] = ko.observable('');
myViewModel[newObservableName](Math.random());
// You can also test it in the console typing
// myViewModel.paragraphXXX('a random text')
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script>
<div id="placeholder">
<p data-bind="text: paragraph0"></p>
</div>
<a id="add" href="#">Add paragraph</a>
C'est une vieille question, mais voici ma réponse, espérons-le, à jour (knockout 3.3.0):
Lorsque vous utilisez des modèles de masquage ou des composants personnalisés pour ajouter des éléments à des collections observables pré-établies, le masquage liera tout automatiquement. Votre exemple ressemble à une collection observable d'éléments de menu qui ferait le travail hors de la boîte.
Basé sur cette réponse existante , j'ai obtenu quelque chose de similaire à vos intentions initiales:
function extendBinding(ko, container, viewModel) {
ko.applyBindings(viewModel, container.children()[container.children().length - 1]);
}
function yourBindingFunction() {
var container = $("#menu");
var inner = $("<select name='list' data-bind='options: listItems'></select>");
container.empty().append(inner);
extendBinding(ko, container, {
listItems: ["item1", "item2", "item3"]
});
}
Voici un JSFiddle pour jouer avec.
Soyez averti, une fois que le nouvel élément fait partie du dom, vous ne pouvez pas le lier à nouveau avec un appel à ko.applyBindings
- c'est pourquoi j'utilise container.empty()
. Si vous devez conserver le nouvel élément et le faire changer au fur et à mesure que le modèle de vue change, passez un observable au paramètre viewModel
de la méthode extendBinding
.
Checkout this answer: How do define a custom knockout 'options binding' with prédéfinies Text and Value options
ko.applyBindingsToNode
est particulièrement utile.