2013-03-01 23 views
16

Utknąłem z tym, co musi być prostą poprawką. Używam knockout.js z zagnieżdżonymi modelami widoku i wszystko wydaje się w porządku, z tym wyjątkiem, że moja funkcja usuwania nie działa poprawnie. Wygląda na to, że jest prawidłowo wiążący, jednak nie jest uruchamiany, gdy kliknę usuń.Model widoku zagnieżdżonego zagnieżdżonego

Dlaczego modele widoku zagnieżdżonego? Długa historia, ale w zasadzie wiele rzeczy jest wymaganych na jednej stronie!

Tak oto kod:

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

I tu jest moje skrzypce: http://jsfiddle.net/6Px4M/2/

Dlaczego więc moja funkcja usuwania nie jest uruchamiana?

Możliwe pytanie poboczne: czy zagnieżdżony widok przedstawia niewłaściwą ścieżkę do nokautu, czy nie ma na ten temat zbyt wiele informacji?

Odpowiedz

23

Jednym z najlepszych sposobów, aby pracować z zagnieżdżonego modelu, jak to jest używać with wiążące. Można to zrobić:

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

Teraz zakres to repeatGuest i można wiązać bezpośrednio przed jego właściwości.

Problem związany z funkcją remove jest powiązany z wartością this i tym, który jest w tej chwili $parent. Funkcje są wykonywane z wartością this, która jest równa bieżącemu zakresowi. Kiedy twoja funkcja usuwania jest powiązana, zasięg jest jednym z obiektów w tablicy date.

Typowym sposobem na obsłużenie tego jest upewnienie się, że twoja funkcja jest zobowiązana do używania zawsze poprawnej wartości this. Można to zrobić w wiązaniu (bardzo brzydki), takich jak:

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

Lepszym rozwiązaniem jest, aby związać go w modelu widoku w konstruktorze RepeatGuest:

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

Umożliwia wdrożenie żyć na prototypie i nadpisuje go w każdym przypadku za pomocą opakowania, które wymusza poprawną wartość this. Ewentualnie, jeśli nie umieścisz go na prototypie, możesz użyć wzoru var self = this; i użyć self w module obsługi.

http://jsfiddle.net/cNdJj/

+0

Wolę wzór 'var self = this'. Łatwiej mi po prostu zawsze używać 'self' niż pamiętać o wiązaniu funkcji. Wygląda też czysto, imo. – Tyrsius

+0

to dobra rzecz, +1 ode mnie, ale istniała bardziej fundamentalna kwestia, że ​​funkcja nigdy nie była uruchamiana, odkąd utknął na prototypie. Chociaż gdyby podążał za wszystkimi twoimi krokami, które również zostaną rozwiązane. –

+0

@Tyrsius - Próbowałem pokazać, jak wciąż możesz umieścić implementację funkcji na prototypie. 'self' to delikatny wzór. –

4

Wiązanie nie będzie miało dostępu do funkcji na niewłaściwym prototypie. Wiążesz teraz z viewModel, a nie z obiektem RepeatGuest.

To działa, jeśli ustawić go jako lokalne funkcji:

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, tak miałem początkowo. Jednak mój viewModel stał się ogromny i próbowałem zmniejszyć jego rozmiar. Czy istnieje inny sposób na usunięcie funkcji usuwania z modelu widoku? –

+0

Oczywiście, zawsze możesz go wyciągnąć i po prostu ustaw "self.removeDate" na nazwę funkcji. –

+1

@ Rozwiązanie RPNiemeyer jest jednak bardziej kompletne. –

Powiązane problemy