如何在.computed()observable中使用knockout的$ parent / $ root伪变量?

时间:2011-12-27 02:52:34

标签: javascript knockout.js

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>

但我的观点模型会变得更复杂,我想“我被选中了吗?”能够做的不仅仅是将单个CSS类添加到单个元素。我真的想在子视图模型上创建一个isSelected计算属性,这样我就可以添加依赖它的其他计算属性。

我尝试编写引用$data$root的JavaScript,因为淘汰赛可能会定义这些变量,并且当它调用我的{{1评估函数:

computed

但没有这样的运气:在评估员{ name: 'Bob', isSelected: ko.computed(function(){ return $data === $root.selectedChild(); }) } 内,function$data都是$root

我还尝试在评估者中使用ko.contextFor,因为它确实可以访问undefined$data。不幸的是,在我的评估函数中,$root也总是返回contextFor。 (无论如何,我对这个策略并没有寄予厚望 - 如果我不得不像这样追逐它的背后,还不清楚淘汰赛如何能够跟踪依赖关系。)

我总是可以在每个子视图模型上手动设置一个属性,该视图返回到父视图模型。但我知道淘汰赛有能力为我做这件事,我想至少在我自己编写之前探讨是否可以使用它的机制。

似乎应该可以将上面的绑定表达式转换为计算的observable - 毕竟,that's what knockout already does

  

另一个巧妙的技巧是声明性绑定只是作为计算的observable实现。

但是当我编写自己的计算可观察量时,如何处理undefined$data伪变量?

3 个答案:

答案 0 :(得分:77)

伪变量仅在数据绑定的上下文中可用。理想情况下,视图模型本身不应该知道或者对显示它的视图有任何依赖性。

因此,在视图模型中添加计算的observable时,您不知道它将如何绑定(如将是$ root)。视图模型或视图模型的一部分甚至可以在不同级别分别绑定到页面的多个区域,因此伪变量将根据您开始的元素而有所不同。

这取决于您要完成的任务,但是如果您希望您的孩子拥有isSelected计算的observable来指示此项目是否与父视图模型上的所选项目相同,那么您将需要找到一种方法让父母可以使用父母。

一个选项是将父级传递给您孩子的构造函数。您甚至不需要将指针添加到父项作为子项的属性,并且可以直接在计算的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 observable的引用传递给子构造函数,如:http://jsfiddle.net/rniemeyer/R5MtC/

如果您的父视图模型存储在全局变量中,那么您可以考虑不将其传递给子项并直接使用它,如:http://jsfiddle.net/rniemeyer/3drUL/。我更喜欢将引用传递给孩子。

答案 1 :(得分:1)

根据我的经验,如果Item在应用程序期间存在,则@RP Niemeyer的答案很好。但如果没有,它可能导致内存泄漏,因为Item的计算observable设置了ViewModel的反向依赖。再一次,如果你永远不会删除任何Item个对象,那就没关系。但是如果你试图摆脱Item,他们就不会收集垃圾,因为淘汰仍然会有反向依赖参考。

可以确保计算出dispose(),可能在Item上的一个cleanup()方法中,当项目消失时调用它,但你必须记住每当删除Item时都会这样做。

相反,为什么不让Item变得不那么聪明,并且在选择它时ViewModel告诉它?只需将Item的{​​{1}}定为旧的可观察对象,然后在isSelected()订阅ViewModel并在该订阅内进行更新。

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

有关详细信息,请参阅同一主题的my recent question答案。

答案 2 :(得分:-7)

在foreach绑定中使用$context代替$parent