knockout data-bind sur des éléments générés dynamiquement

Comment est-il possible de faire fonctionner knockout data-bind sur des éléments générés dynamiquement? Par exemple, j'insère un simple menu html select à l'intérieur d'un div et je veux peupler les options en utilisant la liaison d'options knockout. 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?

42
demandé sur King Julien 2012-06-17 00:19:14

8 réponses

si vous ajoutez cet élément à la volée après avoir lié votre viewmodel, il ne sera pas dans le viewmodel et ne sera pas mis à jour. Vous pouvez faire une des deux choses.

  1. ajouter l'élément au DOM et le re-lier en appelant ko.applyBindings(); à nouveau
  2. ou ajoutez la liste au DOM dès le début et laissez la collection d'options dans votre viewmodel vide. Knockout ne le rendra pas jusqu'à ce que vous ajoutiez des éléments aux options à la volée plus tard.
30
répondu PlTaylor 2017-07-28 14:18:05

knock-out 3.3

ko.bindingHandlers.htmlWithBinding = {
          'init': function() {
            return { 'controlsDescendantBindings': true };
          },
          'update': function (element, valueAccessor, allBindings, viewModel, bindingContext) {
              element.innerHTML = valueAccessor();
              ko.applyBindingsToDescendants(bindingContext, element);
          }
    };

ci-dessus code snippet vous permet d'injecter des éléments html dynamiquement avec la propriété" htmlWithBinding". L'enfant les éléments qui sont ajoutés sont également évalués... c'est-à-dire leurs attributs de liaison de données.

11
répondu Stevanicus 2015-09-10 08:47:26

réécrire le code de liaison html ou en créer un nouveau. Parce que HTML binding empêche les "fixations 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());
  }
};
10
répondu Salomón 2015-04-13 12:38:27

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);
    }
};
4
répondu vivanov 2016-02-26 14:52:23

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 en utilisant myViewModel[newObservable] = ko.observables(")

après ça, rappelez ko.applyBindings.

Voici une page simple où j'ajoute des paragraphes dynamiquement et le nouveau modèle de vue et les reliures 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>
3
répondu Ivan Malagon 2018-02-27 19:14:03

C'est une vieille question, Mais voici ma réponse si tout va bien à jour (K. O. 3.3.0):

lorsque vous utilisez des modèles knockout ou des composants personnalisés pour ajouter des éléments à des collections observables, knockout liera tout automatiquement. Votre exemple ressemble à une collection observable d'éléments de menu ferait le travail hors de la boîte.

1
répondu johanneslink 2015-04-27 18:41:11

basé sur cette réponse existante , j'ai obtenu quelque chose 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 re-lier avec un appel à ko.applyBindings - c'est pourquoi j'utilise container.empty() . Si vous avez besoin de préserver le nouvel élément et de le faire changer au fur et à mesure que le modèle de vue change, passer un observable au paramètre viewModel de la méthode extendBinding .

0
répondu Ivaylo Slavov 2017-05-23 11:55:13

Check-Out cette réponse: comment définir un knockout personnalisé 'options binding' avec un texte prédéfini et des options de valeur

ko.applyBindingsToNode est particulièrement utile.

0
répondu Antoni 2017-11-15 12:44:12