2011-12-27 12 views
59

Wewnątrz wyrażenia wiążącego knockout.js mogę używać $data, $parent, and $root pseudovariables. Jak mogę uzyskać ekwiwalent tych pseudozakładów, gdy używam zadeklarowanego w JavaScript JavaScriptu ko.computed observable?

Mam rodzica viewmodel z kolekcją dzieci, a rodzic viewmodel ma selectedChild obserwowalne. Biorąc pod uwagę, że mogę używać wyrażeń Databinding dodać klasę CSS w zależności od tego dziecko jest aktualnie wybrany: „jestem wybrany”

<ul data-bind="foreach: children"> 
    <li data-bind="text: name, 
        css: {selected: $data === $root.selectedChild()}, 
        click: $root.selectChild"></li> 
</ul> 
<script> 
vm = { 
    selectedChild: ko.observable(), 
    children: [{name: 'Bob'}, {name: 'Ned'}], 
    selectChild: function(child) { vm.selectedChild(child); } 
}; 
ko.applyBindings(vm); 
</script> 

Ale moi ViewModels chce stać się bardziej złożone, i chciałbym aby móc zrobić coś więcej niż tylko dodać pojedynczą klasę CSS do pojedynczego elementu. Naprawdę chcę utworzyć obliczoną właściwość isSelected na podglądzie potomnym, więc mogę następnie dodać inne właściwości obliczane, które zależą od tego.

Próbowałem tylko pisanie JavaScript, który odnosi się do $data i $root na znikoma szansa, że ​​nokaut może określić te zmienne i jakoś trzeba je mieć w zasięgu, gdy zwraca moją funkcję computed przeprowadzenia ewaluacji:

{ 
    name: 'Bob', 
    isSelected: ko.computed(function(){ return $data === $root.selectedChild(); }) 
} 

Ale nie ma szczęścia: w moim oceniającym function, zarówno $data i $rootundefined.

Próbowałem również użyć ko.contextFor w moim ewaluatorze, ponieważ daje dostęp do $data i $root. Niestety, w mojej funkcji ewaluatora, contextFor również zawsze zwraca undefined. (I tak nie spodziewałem się dużej nadziei na tę strategię - nie jest jasne, jak dobrze nokaut byłby w stanie śledzić zależności, gdybym musiał pójść za jego plecami).

Zawsze mogłem ręcznie ustawić właściwość dla każdego potomka viewmodel, który odwołuje się do rodzica viewmodel. Ale wiem, że nokaut ma możliwość zrobienia tego dla mnie i chciałbym przynajmniej zbadać, czy mogę użyć jego mechanizmów zanim zacznę pisać własne.

Wydaje się, że powinno być możliwe, aby przetłumaczyć wyrażenie powyżej wiązania do komputerowej obserwowalne - wszak that's what knockout already does:

Drugi schludny Sztuką jest, że deklaratywne Wiązania są po prostu realizowane jako obliczonych obserwabli.

Ale jak mogę iść o kontaktach z $data i $root pseudovariables gdy piszę moje własne komputerowej zaobserwować?

Odpowiedz

76

Pseudowykłady są dostępne tylko w kontekście powiązania danych. Sam model widoku nie powinien wiedzieć ani mieć żadnych zależności od widoku, który go wyświetla.

Tak więc, dodając obliczane obserwowalne w modelu widoku, nie masz żadnej wiedzy na temat tego, jak będzie on powiązany (jak to, co ma być $ root). Model widoku lub część modelu widoku może być powiązana osobno z wieloma obszarami strony na różnych poziomach, więc pseudo-zmienne będą różne w zależności od elementu, od którego zaczynasz.

To zależy od tego, co próbujesz osiągnąć, ale jeśli chcesz, aby Twoje dziecko miało obliczone obserwowalne isSelected, które wskazuje, czy ten element jest taki sam jak wybrany element w nadrzędnym modelu widoku, musisz znaleźć sposób na udostępnienie rodzicowi dziecka.

Jedną z opcji jest przekazanie rodzica do funkcji konstruktora Twojego dziecka. Nie musisz nawet dodawać wskaźnika do rodzica jako właściwości dziecka i możesz po prostu użyć go w swoim obliczonym obserwowalnym bezpośrednio.

Coś jak:

var Item = function(name, parent) { 
    this.name = ko.observable(name); 
    this.isSelected = ko.computed(function() { 
     return this === parent.selectedItem();   
    }, this); 
}; 

var ViewModel = function() { 
    this.selectedItem = ko.observable(); 
    this.items = ko.observableArray([ 
     new Item("one", this), 
     new Item("two", this), 
     new Item("three", this) 
     ]); 
}; 

Próbka tutaj: http://jsfiddle.net/rniemeyer/BuH7N/

Jeśli wszystko czego dbają o jest wybrany stan, można dostosować go przekazać referencję do selectedItem obserwowalnym do konstruktora jak dziecko : http://jsfiddle.net/rniemeyer/R5MtC/

Jeśli model widoku nadrzędnego jest przechowywany w zmiennej globalnej, można rozważyć nieprzekazanie go do elementu podrzędnego i użycie go bezpośrednio, jak: http://jsfiddle.net/rniemeyer/3drUL/. Wolę jednak przekazać odniesienie do dziecka.

+0

dziękuję za drugi przykład! – vittore

+0

jak nazwałbyś funkcję na root, a także kliknięcie: $ root.selectedItem, w ramach tego samego powiązania? – FutuToad

+0

np. To nie działa: kliknij: function() {$ parent.openAlertDialogueEdit ($ data)} // $ dane wydają się być kopią, a nie rzeczywistą referencją. – FutuToad

-7

Użyj $context zamiast $parent w ramach wiązania foreach.

1

Z mojego doświadczenia wynika, że ​​podejście w odpowiedzi @RP Niemeyera jest w porządku, jeśli Item s żyje przez czas trwania aplikacji. Ale jeśli nie, może to prowadzić do przecieków pamięci, ponieważ obliczone observable Item ustawia odwrotną zależność od ViewModel. Ponownie, to jest ok, jeśli nigdy nie pozbyć się obiektów Item. Ale jeśli spróbujesz pozbyć się Item s, nie będą zbierać śmieci, ponieważ nokaut nadal będzie zawierał referencję do odwrotnej zależności.

Ty mógłby upewnić zbyć() z obliczony, może w metodzie oczyszczania() na Item która jest wywoływana, gdy element odchodzi, ale trzeba pamiętać, aby to zrobić, gdy usunięcie Item s.

Zamiast tego, dlaczego nie uczynić Item trochę mniej inteligentnym i mieć ViewModel powiedzieć, kiedy jest wybrany? Po prostu zrób Item '' ze starego regularnego obserwowalnego, a następnie w ViewModel zasubskrybuj selectedItem i zaktualizuj wewnątrz tej subskrypcji.

Lub użyj @RP Niemeyer's pub/sub solution. (Aby być sprawiedliwym, rozwiązanie to pojawiło się po jego odpowiedzi tutaj.) Będziesz jednak musiał oczyścić, ponieważ tworzy też odwrotne zależności. Ale przynajmniej jest mniej sprzężeń.

Zobacz odpowiedź na my recent question na ten sam temat, aby uzyskać więcej informacji.

Powiązane problemy