javascript-如何从可观察的.computed()内部使用敲除的$ parent / $ root伪变量?

在基因敲除.js绑定表达式中,可以使用2562075775528828862720、$parent$root伪变量。 当我使用JavaScript中声明的$data observable时,如何获得这些伪变量的等价物?

我有一个带有子级集合的父视图模型,并且该父视图模型具有可观察到的$data。 鉴于此,我可以使用数据绑定表达式将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类添加到单个元素中,还可以做更多的事情。 我真的很想在子viewmodel上创建$data计算属性,因此可以添加依赖于它的其他计算属性。

我试过只编写引用$data$root的JavaScript,但有一次机会,敲除可能会定义这些变量,并在调用我的contextFor评估程序函数时使它们处于范围内:

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

但是没有这样的运气:在我的评估器$data中,$rootcontextFor都是undefined

我还尝试在评估器中使用$data,因为它确实可以访问$data$root。不幸的是,在评估器函数中,contextFor也始终返回undefined。 尚不清楚如果我必须像这样落后,淘汰赛能够很好地跟踪依赖项。)

我总是可以在每个引用到父视图模型的子视图模型上手动设置属性。 但是我知道淘汰赛有能力为我做到这一点,并且我至少想在写自己的文章之前探索一下是否可以使用它的机制。

似乎应该可以将上述绑定表达式转换为可计算的可观察值了-毕竟,这就是敲除的作用:

另一个巧妙的技巧是声明式绑定仅作为计算的可观察对象实现。

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

Joe White asked 2019-10-09T19:29:48Z
3个解决方案
77 votes

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

因此,在视图模型中添加计算的可观察对象时,您不知道它将如何绑定(例如将成为$ root)。 视图模型或视图模型的一部分甚至可以分别绑定到页面的不同级别的多个区域,因此伪变量会有所不同,具体取决于您开始使用的元素。

这取决于您要完成的工作,但是如果您希望孩子有一个计算得出的selectedItem可观察值,以指示该项目是否与父视图模型上的选定项目相同,那么您将需要找到一种方法 父母对孩子可用。

一种选择是将父级传递给孩子的构造函数。 您甚至不需要将指向父对象的指针添加为子对象的属性,而可以直接在您计算出的可观察对象中使用它。

就像是:

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/。]我更喜欢将引用传递给 这个孩子。

RP Niemeyer answered 2019-10-09T19:30:48Z
1 votes

以我的经验,如果有Items在申请期间还活着,@ RP Niemeyer的回答就很好。 但是,如果不是这样,则可能导致内存泄漏,因为ViewModel的计算得出的可观察值与Item建立了反向依赖关系。同样,如果您从未摆脱任何isSelected()对象,那就可以了。 但是,如果您设法摆脱ViewModels,它们将不会被垃圾收集,因为敲除仍然会包含该反向依赖关系引用。

您可以确保对计算出的对象进行dispose(),也许在Item上的cleanup()方法中,该项目消失后即会调用该方法,但是您必须记住,每当删除ViewModels时都要这样做。

相反,为什么不让Item变得更聪明一些,并让ViewModel告诉它何时被选中? 只需将ItemisSelected()设为常规的旧可观察对象,然后在ViewModel中订阅selectedItem并在该订阅中进行更新即可。

或者,使用@RP Niemeyer的发布/订阅解决方案。 (公平地说,此解决方案是在他的回答之后提出的。)不过,您仍然需要清理,因为它也会创建反向依赖关系。 但至少耦合较少。

有关更多详细信息,请参见我对同一主题的最新问题的答案。

devguydavid answered 2019-10-09T19:31:43Z
-7 votes

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

Peter answered 2019-10-09T19:32:10Z
translate from https://stackoverflow.com:/questions/8640748/how-can-i-use-knockouts-parent-root-pseudovariables-from-inside-a-computed