web-dev-qa-db-fra.com

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

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?

45
King Julien

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.

  1. Ajouter l'élément au DOM et le lier à nouveau en appelant à nouveau ko.applyBindings();
  2. OU ajoutez la liste au DOM depuis le début et laissez la collection d'options dans votre modèle de vue vide. Knockout ne le rendra que lorsque vous ajouterez des éléments aux options à la volée plus tard.
31
PlTaylor

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.

12
Stevanicus

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());
  }
};
10
Salomón

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
vivanov

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>
3
Ivan Malagon

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.

2
johanneslink

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.

0
Ivaylo Slavov

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.

0
Antoni