2011-12-27 60 views
59

knockout.js綁定表達式中,我可以使用$data, $parent, and $root pseudovariables。當我使用在JavaScript中聲明的ko.computed observable時,如何獲得這些僞變量的等價物?

我有一個父視圖模型與一個孩子的集合,父視圖模型有一個selectedChild觀察。鑑於這種情況,我可以使用數據綁定表達式添加到任何一個孩子正在選擇一個CSS類:「我會選擇」

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

但我的ViewModels會得到比較複雜,我想能夠做的不僅僅是將單個CSS類添加到單個元素。我真的想在子viewmodel上創建一個isSelected計算屬性,這樣我就可以添加其他依賴於它的計算屬性。

我已經嘗試了編寫JavaScript,指的$data$root,在關閉的機會,淘汰賽可能定義這些變量並以某種方式讓他們在範圍上時,它調用我computed計算器功能:

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

但沒有這樣的運氣:在我的評估者function裏面,$data$root都是undefined

我也嘗試在我的評估程序中使用ko.contextFor,因爲它確實可以訪問$data$root。不幸的是,在我的評估器功能中,contextFor也總是返回undefined。 (無論如何,我並沒有對這個策略寄予厚望 - 如果我必須像這樣背對背,那麼淘汰賽將會如何跟蹤依賴關係還不清楚。)

我總是可以手動設置屬性,每個子視圖模型引用父視圖模型。但我知道淘汰賽有能力爲我做這件事,而且我想至少在我寫自己的作品之前先探討一下我是否可以使用它的機制。

現在看來似乎應該是可以翻譯上面綁定表達式的計算觀察到的 - 畢竟,that's what knockout already does

另一個絕招是聲明綁定簡單地計算觀測實現。

但我如何去與$data$root pseudovariables打交道時,我在寫我自己的計算觀察到的?

回答

76

僞變量僅在數據綁定的情況下可用。視圖模型本身理想上不應該知道或顯示視圖的任何依賴關係。

因此,當在視圖模型中添加計算的observables時,您不知道它將如何被綁定(比如什麼是$ root)。視圖模型或視圖模型的一部分甚至可以分別綁定到不同級別的頁面的多個區域,因此根據您開始使用的元素,僞變量會有所不同。

這取決於你想要完成什麼,但是如果你想要你的孩子有一個isSelected計算的觀測值,它指示這個項目是否與父視圖模型上選擇的項目相同,那麼你將需要找到一種讓父母可以得到的方法。

一種選擇是將父項傳遞給子項的構造函數。您甚至不需要將指針添加到父級作爲子級的屬性,並且可以直接在計算的observable中使用它。

喜歡的東西:

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

樣品在這裏:http://jsfiddle.net/rniemeyer/BuH7N/

如果所有你關心的是所選擇的狀態,那麼你就可以調整它傳遞給selectedItem可觀察到孩子的構造就像一個參考:http://jsfiddle.net/rniemeyer/R5MtC/

如果你的父視圖模型存儲在一個全局變量中,那麼你可以考慮不將它傳遞給子代並直接使用它:http://jsfiddle.net/rniemeyer/3drUL/。儘管如此,我更願意將參考傳遞給孩子。

+0

謝謝你的第二個例子! – vittore 2011-12-27 21:57:38

+0

如何在root上調用函數以及在相同的綁定中單擊$ root.selectedItem? – FutuToad 2013-03-13 16:52:55

+0

例如,這不起作用:點擊:函數(){$ parent.openAlertDialogueEdit($ data)} // $數據似乎是一個副本,而不是實際的參考 – FutuToad 2013-03-13 16:54:13

-7

在foreach綁定中使用$context而不是$parent

1

根據我的經驗,如果Item在應用程序期間生效,@RP Niemeyer的答案中的方法就沒有問題。但是,如果不是,它可能導致內存泄漏,因爲Item的可計算可觀察性設置與ViewModel的反向依賴關係。再說一次,如果你永遠不會擺脫任何Item對象。但是如果你試圖擺脫Item s他們不會收集垃圾,因爲淘汰賽仍然會有反向依賴參考。

可以確保處置()的計算,也許在Item清理()方法時,該項目消失被調用,但你要記得做,只要去除Item秒。

相反,爲什麼不使Item不那麼聰明,並且ViewModel它被選中時會告訴它?只要Item的​​是一個普通的老觀察者,然後在ViewModel訂閱selectedItem並在該訂閱內更新。

或者,使用@RP Niemeyer的pub/sub solution。 (公平地說,這個解決方案是在他回答完這個問題後出現的。)但是,你仍然需要清理,因爲它也會產生逆向依賴。但至少有更少的耦合。

有關更多詳細信息,請參閱my recent question對此同一主題的回答。