2012-06-16 15 views
40

Come è possibile eseguire il lavoro di bind di dati vincolati su elementi generati dinamicamente? Ad esempio, inserisco un semplice menu di selezione html all'interno di un div e voglio popolare le opzioni usando il binding delle opzioni ad eliminazione diretta. Ecco come appare il mio codice:knockout data-bind su elementi generati dinamicamente

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

ma questo metodo non funziona. Qualche idea?

+0

stai aggiungendo questo dopo aver eseguito ko.applyBindings (yourVMHere); – PlTaylor

+0

Eliminare l'idea del binding automatico (KO) su questo elemento DOM aggiunto dinamicamente e gestirlo manualmente. – Vaibhav

risposta

30

Se si aggiunge questo elemento al volo dopo aver associato il proprio viewmodel, non sarà visibile nel viewmodel e non si aggiornerà. Puoi fare una delle due cose.

  1. aggiungere l'elemento al DOM e re-bind chiamando ko.applyBindings(); nuovo
  2. o aggiungere l'elenco per il DOM fin dall'inizio e lasciare la collezione opzioni nel ViewModel vuota. Knockout non lo renderizza fino a quando non aggiungerai elementi alle opzioni in un secondo momento.
+11

Se richiamo di nuovo applyBindings, si genera un errore: Errore: non è possibile applicare più binding allo stesso elemento. – Chris

+2

Questa deve essere una nuova funzionalità dei framework più recenti. La seconda opzione è ancora valida, e onestamente l'opzione migliore per cominciare. – PlTaylor

+1

Sì, ho immaginato tanto, è una cattiva pratica applicare i binding più di una volta su un elemento. Come si spara 2 volte, penso che sia per questo che hanno aggiunto alcuni avvertimenti in KO 3 – Chris

3

EDIT: Sembra che questo non funziona a partire dalla versione 2.3 IIRC come sottolineato dal LosManos

È possibile aggiungere un altro osservabile al modello vista utilizzando myViewModel [newObservable] = ko.observable (' ')

Successivamente, richiamare nuovamente su ko.applyBindings.

Ecco una semplice pagina in cui aggiungo i paragrafi dinamicamente e il nuovo modello di visualizzazione e le associazioni funzionano perfettamente.

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

Chiamare applyBindings più di una volta non è una buona idea. – gliljas

+0

@ gunteman..perché no? – lamarant

+3

Questo sembra lanciare lo stesso errore "impossibile applicare i binding due volte". È possibile dire a ko.applyBindings() un elemento molto specifico che vuoi aggiungere ai binding? – Chris

0

Sulla base di this existing answer, ho achived qualcosa di simile alle vostre intenzioni iniziali:

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

Ecco un JSFiddle con cui giocare.

Attenzione, una volta che il nuovo elemento è parte del dom, non è possibile re-associarlo con una chiamata a ko.applyBindings - è per questo che io uso container.empty(). Se è necessario conservare il nuovo elemento e modificarlo mentre il modello di visualizzazione cambia, passare un parametro osservabile al parametro viewModel del metodo extendBinding.

10

riscrivere il codice di associazione html o crearne uno nuovo. Perché html legame impedisce "iniettato binding" in html dinamico:

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

Dopo aver esaminato tutte le risposte e i commenti su questa discussione, IMO questa è in realtà la soluzione migliore alla domanda "knockout data-bind su elementi generati dinamicamente". Buona soluzione! – MattSizzle

1

Si tratta di una vecchia questione, ma ecco la mia spera up-to-date risposta (knockout 3.3.0):

Quando si utilizzano i modelli ad eliminazione diretta o componenti personalizzati per aggiungere elementi alle raccolte osservabili prebound, il knockout associa automaticamente tutto. Il tuo esempio sembra una raccolta osservabile di voci di menu che farebbero il lavoro immediatamente.

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

Sopra frammento di codice consente di iniettare elementi HTML in modo dinamico con la proprietà "htmlWithBinding". Gli elementi figli che vengono aggiunti vengono quindi valutati ... vale a dire i loro attributi di associazione dei dati.

3

Per v3.4.0 utilizzare il binding personalizzato di seguito:

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