2012-06-16 4 views
40

Wie ist es möglich, Knockout-Datenbindungsarbeiten an dynamisch generierten Elementen durchzuführen? Zum Beispiel füge ich ein einfaches html select-Menü in ein div ein und möchte Optionen mit den knockout-Optionen binden. So sieht mein Code aus:Knockout-Datenbindung an dynamisch generierten Elementen

$('#menu').html('<select name="list" data-bind="options: listItems"></select>'); 

aber diese Methode funktioniert nicht. Irgendwelche Ideen?

+0

fügen Sie dies, nachdem Sie Ihre ko.applyBindings getan haben (yourVMHere); – PlTaylor

+0

Lassen Sie die Idee der (automatischen) KO-Bindung auf dieses dynamisch hinzugefügte DOM-Element fallen und behandeln Sie es manuell. – Vaibhav

Antwort

30

Wenn Sie dieses Element im laufenden Betrieb hinzufügen, nachdem Sie Ihr Ansichtsmodell gebunden haben, wird es nicht im Ansichtsmodell angezeigt und nicht aktualisiert. Sie können eines von zwei Dingen tun.

  1. Fügen Sie das Element auf das DOM und neu binden, indem es ko.applyBindings(); wieder
  2. oder fügen Sie die Liste an den DOM von Anfang aufrufen und die Optionen Sammlung in Ihrem Viewmodel leer lassen. Knockout rendert es erst, wenn Sie später Optionen zu Optionen hinzufügen.
+11

Wenn ich applyBindings erneut aufrufen, wird ein Fehler ausgegeben: Fehler: Sie können Bindungen nicht mehrfach auf dasselbe Element anwenden. – Chris

+2

Das muss ein neues Merkmal der neueren Frameworks sein. Die zweite Option ist immer noch praktikabel, und ehrlich gesagt, die bessere Option, um damit zu beginnen. – PlTaylor

+1

Ja, ich denke, es ist eine schlechte Übung, Bindungen mehr als einmal auf ein Element anzuwenden. Da es 2 mal feuern wird, denke ich, deshalb haben sie einige Warnungen in KO 3 – Chris

3

EDIT: Es scheint, dass dies nicht seit Version 2.3 IIRC nach wie wies funktioniert LosManos

Sie einen anderen beobachtbaren zu Ihrer Ansicht Modell hinzufügen können mit myViewModel [newObservable] = ko.observable (‘ ')

Danach rufen Sie erneut auf ko.applyBindings.

Hier ist eine einfache Seite, wo ich Absätze dynamisch hinzufügen und das neue Ansichtsmodell und die Bindungen einwandfrei funktionieren.

// myViewModel starts only with one observable 
 
    \t var myViewModel = { 
 
    \t  paragraph0: ko.observable('First') 
 
    \t }; 
 
    
 
    \t var count = 0; 
 
    
 
    \t $(document).ready(function() { 
 
    \t \t ko.applyBindings(myViewModel); 
 
    
 
    \t \t $('#add').click(function() { 
 
    \t \t \t // Add a new paragraph and make the binding 
 
    \t \t \t addParagraph(); 
 
    \t \t \t // Re-apply! 
 
    \t \t \t ko.applyBindings(myViewModel); \t \t \t 
 
    \t \t \t return false; \t 
 
    \t \t }); 
 
    \t }); 
 
    
 
    \t function addParagraph() { 
 
    \t \t count++; 
 
    \t \t var newObservableName = 'paragraph' + count; 
 
    \t  $('<p data-bind="text: ' + newObservableName + '"></p>').appendTo('#placeholder'); 
 
    \t \t 
 
    \t  // Here is where the magic happens 
 
    \t \t myViewModel[newObservableName] = ko.observable(''); 
 
    \t \t myViewModel[newObservableName](Math.random()); 
 
    
 
    \t \t // You can also test it in the console typing 
 
    \t \t // myViewModel.paragraphXXX('a random text') 
 
    \t }
<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>

+7

hinzugefügt ApplyBindings mehr als einmal aufrufen ist keine gute Idee. – gliljas

+0

@ gunteman..warum nicht? – lamarant

+3

Dies scheint den gleichen "Fehler kann Bindungen nicht zweimal anwenden" Fehler zu werfen. Ist es möglich, ko.applyBindings() ein sehr spezifisches Element zu nennen, das Sie den Bindungen hinzufügen möchten? – Chris

0

Basierend auf this existing answer habe ich etwas Ähnliches wie Ihre ursprünglichen Absichten achived:

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"] 
    }); 
} 

hier ein JSFiddle ist zu spielen.

Seien Sie gewarnt, sobald das neue Element Teil des Doms ist, können Sie es nicht mit einem Aufruf an ko.applyBindings neu binden - deshalb verwende ich container.empty(). Wenn Sie das neue Element beibehalten und es ändern müssen, während sich das Ansichtsmodell ändert, übergeben Sie eine Observable an den Parameter viewModel der Methode extendBinding.

10

HTML-Bindungscode neu schreiben oder neu erstellen. Da HTML-Bindung verhindert "injizierten Bindungen" in dynamischer html:

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()); 
 
    } 
 
};

+3

Nachdem ich alle Antworten und Kommentare zu diesem Thema gelesen habe, ist IMO die beste Lösung für die Frage "knockout data-bind on dynamisch generierte Elemente". Gute Lösung! – MattSizzle

1

Es ist eine alte Frage, aber hier ist meine hoffentlich up-to-date Antwort (Knockout 3.3.0):

Bei Verwendung von Knockout-Vorlagen oder benutzerdefinierten Komponenten zum Hinzufügen von Elementen zu vorgebundenen beobachtbaren Sammlungen wird knockout alles automatisch binden. Ihr Beispiel sieht aus wie eine beobachtbare Sammlung von Menüelementen würde die Aufgabe out of the Box erledigen.

10

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); 
      } 
    }; 

Above Code-Schnipsel können Sie HTML-Elemente dynamisch mit dem "htmlWithBinding" Eigenschaft injizieren. Die untergeordneten Elemente, die hinzugefügt werden, werden dann ebenfalls ausgewertet ... d. H. Ihre Datenbindungsattribute.

3

Für v3.4.0 verwenden, um die benutzerdefinierten unten Bindung:

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); 
    } 
};