我怎样才能从.computed()可观察到的内部使用knockout的$ parent / $ root伪variables?

在knockout.js绑定expression式中,我可以使用$data$parent$root伪variables 。 当我使用在JavaScript中声明的ko.computed可观察值时,如何获得这些伪variables的等价物?

我有一个父视图模型与一个孩子的集合,父视图模型有一个selectedChild可观察。 鉴于此,我可以使用数据绑定expression式来添加一个CSS类到当前select的任何一个孩子:

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

但是我的视angular模型会变得越来越复杂,我想要“我select了吗?” 能够做的不仅仅是将单个CSS类添加到单个元素。 我真的想要一个isSelected计算属性的子视图模型,所以我可以添加其他计算属性,依赖于它。

我已经尝试过写JavaScript引用$data$root ,在knockout可能定义这些variables的时候,当它调用我的computed评估函数时,它们在某种程度上处于范围之内:

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

但是没有这样的运气:在我的评估function$data$root都是undefined

我也试过在评估器中使用ko.contextFor ,因为它确实可以访问$data$root 。 不幸的是,在我的评估函数中, contextFor也总是返回undefined 。 (无论如何,我并没有抱着这个策略的高度希望 – 如果我必须像这样背对背,那么现在还不清楚淘汰赛能够如何跟踪这些依赖关系。)

我总是可以在每个引用父视图模型的子视图模型上手动设置一个属性。 但是我知道淘汰赛有能力为我做这件事,而且我想至less在我写作自己的作品之前先探讨一下是否可以使用它的机制。

似乎应该可以将上面的绑定expression式转换为一个计算的可观察值 – 毕竟, 这是淘汰赛已经做的事情 :

另一个巧妙的技巧是声明式绑定只是作为计算的观察值来实现。

但是当我编写自己的计算观察值时,如何处理$data$root伪variables呢?

伪variables仅在数据绑定的情况下可用。 视图模型本身理想情况下不应该知道或显示视图的任何依赖关系。

所以,当在视图模型中添加计算的观测值时,你不知道它将如何被绑定(比如什么是$ root)。 视图模型或视图模型的一部分甚至可以分别绑定到不同层次的页面的多个区域,所以伪variables将根据您所在的元素而不同。

这取决于你想要完成什么,但是如果你想要你的孩子有一个isSelected计算的观察值,它指示这个项目是否与父视图模型上select的项目相同,那么你将需要find一种方法来使父母可以为孩子。

一种select是将父项传递给子项的构造函数。 您甚至不需要将指针添加到父级作为子级的属性,并且可以直接在计算的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/

如果你所关心的只是被select的状态,那么你可以调整它来将对selectedItem observable的引用传递给子构造函数,如: http : //jsfiddle.net/rniemeyer/R5MtC/

如果你的父视图模型存储在一个全局variables中,那么你可以考虑不把它传递给孩子,直接使用它: http : //jsfiddle.net/rniemeyer/3drUL/ 。 我宁愿把这个引用传给孩子。

根据我的经验,如果Item在应用程序的持续时间内活动,那么@RP Niemeyer的回答就没有问题了。 但是,如果不是,它可能会导致内存泄漏,因为Item的可观测值与ViewModelbuild立了反向依赖关系。 再说一次,如果你永远不会摆脱任何Item对象。 但是,如果你试图摆脱ItemS,他们将不会收集垃圾,因为淘汰赛将仍然有反向依赖的参考。

可以确保把计算的东西放在()中,也许在项目消失时被调用的Item的cleanup()方法中,但是每当移除Item时都要记得这样做。

相反,为什么不让Itemless一些智能,并让ViewModel在select时告诉它? 只要让ItemisSelected()是一个普通的老观察者,然后在ViewModel订阅selectedItem并在那个订阅里更新。

或者,使用@RP Niemeyer的pub / sub解决scheme 。 (公平地说,这个解决scheme是在他回答这个问题之后出现的)。但是,你仍然需要清理,因为它也会产生逆向依赖。 但至less有更less的耦合。

有关更多详细信息,请参阅我最近关于同一主题的问题的答案。

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