2012-06-16 17 views
40

Jak można wykonać nokautowanie danych przy użyciu dynamicznie generowanych elementów? Na przykład wstawiam proste menu wyboru html do elementu div i chcę zapełnić opcje za pomocą wiązania opcji knockout. Tak wygląda mój kod:knockout data-bind na dynamicznie generowanych elementach

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

, ale ta metoda nie działa. Jakieś pomysły?

+0

dodajesz to po wykonaniu ko.applyBindings (yourVMHere); – PlTaylor

+0

Upuść pojęcie wiązania (automatycznego) KO na dynamicznie dodanym elemencie DOM i obsłuż go ręcznie. – Vaibhav

Odpowiedz

30

Jeśli dodasz ten element w locie po związaniu widoku, nie będzie on w widoku i nie będzie aktualizowany. Możesz zrobić jedną z dwóch rzeczy.

  1. Dodaj element do DOM i ponownie związać to poprzez wywołanie ko.applyBindings(); ponownie
  2. lub dodać do listy DOM od początku i pozostawić zbiór opcji w viewmodel pusty. Knockout nie wyrenderuje go, dopóki nie dodasz elementów do opcji w locie później.
+11

Jeśli ponownie wywołać applyBindings, rzut jest błędem: Błąd: Nie można zastosować powiązań wiele razy do tego samego elementu. – Chris

+2

To musi być nowa cecha nowszych frameworków. Druga opcja jest nadal opłacalna i, szczerze mówiąc, lepsza opcja na początek. – PlTaylor

+1

Tak, pomyślałem, że złym zwyczajem jest stosowanie wiązań więcej niż jeden raz na elemencie. Jak to wystrzeli 2 razy, myślę, że dlatego dodali ostrzeżenia w KO 3 – Chris

3

EDIT: Wydaje się, że to nie działa od wersji 2.3 IIRC jak wskazał przez LosManos

Możesz dodać kolejny obserwowalne do widoku modelu za pomocą myViewModel [newObservable] = ko.observable (” ')

Następnie wywołaj ponownie do ko.applyBindings.

Oto prosta strona, na której dynamicznie dodawam akapity, a nowy model widoku i powiązania działają bezbłędnie.

// 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

Wywołanie applyBindings więcej niż raz nie jest dobrym pomysłem. – gliljas

+0

@ gunteman..dlaczego nie? – lamarant

+3

Wydaje się, że ten sam błąd "nie można zastosować podwójnych powiązań". Czy można powiedzieć ko.applyBindings() bardzo konkretny element, który chcesz dodać do powiązań? – Chris

0

podstawie this existing answer, mam VSD coś podobnego do swoich początkowych zamierzeń:

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

Oto JSFiddle do zabawy.

Ostrzegam, po tym, jak nowy element jest częścią domeny, nie można go ponownie powiązać z numerem telefonu ko.applyBindings - dlatego używam container.empty(). Jeśli zachodzi potrzeba zachowania nowego elementu i zmiany go w miarę zmiany modelu widoku, należy przekazać obserwowalny parametr viewModel metody .

10

przepisać kod wiążący html lub utworzyć nowy. Ponieważ html wiązanie zapobiega "wstrzykuje powiązania" w dynamiczny 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

Po przeanalizowaniu wszystkich odpowiedzi i komentarzy na temat tego wątku, IMO jest to w rzeczywistości najlepsze rozwiązanie dla pytania "knockout data-bind na dynamicznie generowanych elementach". Dobre rozwiązanie! – MattSizzle

1

To stare pytanie, ale tutaj jest mój nadzieją up-to-date odpowiedzi (knockout 3.3.0):

Podczas korzystania z szablonów nokautowych lub niestandardowych komponentów w celu dodawania elementów do wcześniej obserwowalnych kolekcji, nokaut wiąże wszystko automatycznie. Twój przykład wygląda jak obserwowalna kolekcja elementów menu, która wykona zadanie po wyjęciu z pudełka.

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

Powyżej fragment kodu pozwala wprowadzić elementy html dynamicznie z „htmlWithBinding” własności. Elementy potomne, które są dodawane, są również oceniane ... to jest ich atrybuty wiązania danych.

3

Dla v3.4.0 używać wiązania poniżej niestandardowe:

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); 
    } 
}; 
Powiązane problemy