2013-03-01 14 views
16

Sono bloccato con quello che deve essere una soluzione semplice. Sto usando knockout.js con i modelli di visualizzazione annidati, e tutto sembra a posto, tranne che la mia funzione di rimozione non funziona correttamente. Sembra che sia vincolante correttamente, tuttavia non viene attivato quando faccio clic su Rimuovi.Knockout nested view model

Perché i modelli di visualizzazione annidati? Lunga storia, ma in sostanza è necessario un sacco di cose per essere su una pagina!

Quindi, ecco il codice:

HTML

<section class="mini-form-container"> 
    <form data-bind="submit: repeatGuest.addDate"> 
     <input type="date" data-bind="value: repeatGuest.previousStay"/> 
     <button type="submit" class="button-secondary ">Add date</button> 
    </form> 
    <div data-bind="foreach: repeatGuest.dates, visible: repeatGuest.dates().length > 0"> 
     <div> 
      <input data-bind="value: date" disabled="disabled" /> 
      <a data-bind="click: $parent.removeDate">Remove</a> 
     </div> 
    </div> 
</section> 

<section> 
    <div data-bind="text: ko.toJSON($data)"></div> 
</section> 

Javascript

function RepeatGuest() { 
    /// <summary>Child View Model</summary> 
    this.dates = ko.observableArray(); 
    this.previousStay = ko.observable(); 
} 

RepeatGuest.prototype.addDate = function() { 
     var self = this.repeatGuest; 
     if (self.previousStay()) { 
      self.dates.push({ 
       date: self.previousStay() 
      }); 
     } 
    }; 

RepeatGuest.prototype.removeDate = function (date) { 
    this.dates.remove(date); 
} 

function ViewModel() { 
    var self = this; 
    self.repeatGuest = new RepeatGuest(); 
} 
ko.applyBindings(new ViewModel()); 

E qui è il mio violino: http://jsfiddle.net/6Px4M/2/

Quindi, perché la mia funzione di rimozione non viene licenziata?

Possibile domanda a lato: i modelli di vista nidificati indicano il percorso sbagliato da prendere a eliminazione diretta, non sembrano molte informazioni su questo?

risposta

23

Uno dei migliori modi di lavorare con un modello nidificato come questo è quello di utilizzare il with vincolante. Si può fare:

<div data-bind="with: repeatGuest"> 
    ... 
</div> 

Ora, il campo di applicazione è la vostra repeatGuest ed è possibile associare direttamente contro le sue proprietà.

Il problema con la tua funzione remove è correlato al valore di this e chi è $parent in quel momento. Le funzioni vengono eseguite con un valore di this uguale all'ambito corrente. Quando viene associata la funzione di rimozione, l'ambito è uno degli oggetti nell'array date.

Il modo tipico per gestire questo è assicurarsi che la funzione sia destinata a utilizzare sempre il valore corretto di this. Ciò potrebbe essere fatto nel legame (molto brutta) come:

<a data-bind="click: $parent.repeatGuest.removeDate.bind($parent.repeatGuest)">Remove</a> 

Una soluzione migliore è quella di legare nel modello di vista, nella vostra RepeatGuest costruttore:

this.removeDate = this.removeDate.bind(this); 

Questo permette l'implementazione di vivere sul prototipo e lo sovrascrive su ogni istanza con un wrapper che impone il valore corretto di this. In alternativa, se non lo si inserisce nel prototipo, è possibile utilizzare lo schema e utilizzare self nel gestore.

http://jsfiddle.net/cNdJj/

+0

Preferisco il modello 'var auto = this'. È più facile per me usare sempre "sé" piuttosto che ricordare di legare le funzioni. Sembra anche più pulito, imo. – Tyrsius

+0

questa roba buona, +1 da parte mia, ma c'era un problema più fondamentale che la funzione non era mai stata eseguita da quando l'ha incollata sul prototipo. Anche se se avesse seguito tutti i tuoi passi si sarebbe risolto. –

+0

@Tyrsius - Stavo cercando di mostrare come è ancora possibile posizionare l'implementazione della funzione sul prototipo. 'self' è un modello raffinato. –

4

Il binding non accederà alla funzione sul prototipo errato. Stai vincolando a viewModel, non all'oggetto RepeatGuest in questo momento.

Funziona se si imposta come una funzione locale:

http://jsfiddle.net/6Px4M/3/

function ViewModel() { 
    var self = this; 
    self.repeatGuest = new RepeatGuest(); 
    self.removeDate = function (date) { 
     self.repeatguest.dates.remove(date); 
    } 
} 
+0

Ah, questo è il modo che ho avuto in origine. Tuttavia il mio viewModel è diventato massiccio e stavo cercando di ridimensionare le sue dimensioni. C'è un altro modo per ottenere la funzione di rimozione dal modello di visualizzazione? –

+0

Certo, puoi sempre estrarlo e basta impostare 'self.removeDate' sul nome della funzione. –

+1

@ La soluzione di RPNiemeyer è più completa però. –

Problemi correlati