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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8640748/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 06:51:43  来源:igfitidea点击:

How can I use knockout's $parent/$root pseudovariables from inside a .computed() observable?

javascriptknockout.js

提问by Joe White

Inside a knockout.jsbinding expression, I can use the $data, $parent, and $rootpseudovariables. How can I get the equivalent of those pseudovariables when I'm using a ko.computedobservabledeclared in JavaScript?

knockout.js绑定表达式,我可以使用$data$parent$rootpseudovariables。当我使用JavaScript 中声明的ko.computedobservable时,如何获得这些伪变量的等价物?

I've got a parent viewmodel with a collection of children, and the parent viewmodel has a selectedChildobservable. Given that, I can use databinding expressions to add a CSS class to whichever child is currently selected:

我有一个带有一组孩子的父视图模型,父视图模型有一个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>

But my viewmodels are going to get more complex, and I'd like "am I selected?" to be able to do more than just adding a single CSS class to a single element. I really want to make an isSelectedcomputed property on the child viewmodel, so I can then add other computed properties that depend on it.

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

I've tried just writing JavaScript that refers to $dataand $root, on the off-chance that knockout might define those variables and somehow have them be in scope when it calls my computedevaluator function:

我试过只编写引用$dataand 的JavaScript $root,在这种情况下,淘汰赛可能会定义这些变量,并在调用我的computed评估器函数时以某种方式让它们在范围内:

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

But no such luck: inside my evaluator function, both $dataand $rootare undefined.

但没有这样的运气:我的评估中function,无论是$data$rootundefined

I've also tried using ko.contextForinside my evaluator, since it does give access to $dataand $root. Unfortunately, inside my evaluator function, contextForalso always returns undefined. (I didn't hold out high hopes for this strategy anyway -- it's not clear how well knockout would be able to track the dependencies if I had to go behind its back like this.)

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

I could always manually set a property on each child viewmodel that refers back to the parent viewmodel. But I know that knockout has the ability to do this for me, and I'd like to at least explore whether I can use its mechanisms before I go writing my own.

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

It seems like it should be possible to translate the above binding expression to a computed observable -- after all, that's what knockout already does:

似乎应该可以将上述绑定表达式转换为计算出的 observable —— 毕竟,这就是淘汰赛已经做到的

The other neat trick is that declarative bindings are simply implemented as computed observables.

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

But how do I go about dealing with the $dataand $rootpseudovariables when I'm writing my own computed observable?

但是$data$root当我编写自己的计算 observable 时,我该如何处理和伪变量呢?

回答by RP Niemeyer

The pseudovariables are only available in the context of data binding. The view model itself ideally should not know about or have any dependencies on the view that is displaying it.

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

So, when adding computed observables in the view model, you have no knowledge of how it will be bound (like what is going to be $root). A view model or part of a view model could even be bound separately to multiple areas of the page at different levels, so the pseudo-variables would be different depending on the element that you are starting with.

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

It depends on what you are trying to accomplish, but if you want your child to have an isSelectedcomputed observable that indicates whether this item is the same as the selected item on the parent view model, then you will need to find a way to make the parent available to the child.

这取决于你想要完成什么,但是如果你想让你的孩子有一个isSelected计算的 observable 来指示这个项目是否与父视图模型上的选定项目相同,那么你需要找到一种方法来使父级可供孩子使用。

One option is to pass the parent into the constructor function of your child. You do not even need to add the pointer to the parent as a property of the child and can just use it in your computed observable directly.

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

Something like:

就像是:

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

Sample here: http://jsfiddle.net/rniemeyer/BuH7N/

此处示例:http: //jsfiddle.net/rniemeyer/BuH7N/

If all you care about is the selected status, then you can tweak it to pass a reference to the selectedItemobservable to the child constructor like: http://jsfiddle.net/rniemeyer/R5MtC/

如果您只关心所选状态,那么您可以调整它以将selectedItemobservable的引用传递给子构造函数,例如:http: //jsfiddle.net/rniemeyer/R5MtC/

If your parent view model is stored in a global variable, then you could consider not passing it to the child and using it directly like: http://jsfiddle.net/rniemeyer/3drUL/. I prefer to pass the reference to the child though.

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

回答by devguydavid

In my experience the approach in @RP Niemeyer's answer is fine if Items live for the duration of the application. But if not, it can lead to memory leaks, because Item's computed observable sets up a reverse dependency from the ViewModel. Again, that's ok if you never get rid of any Itemobjects. But if you do try to get rid of Items they won't get garbage collected because knockout will still have that reverse dependency reference.

根据我的经验,@RP Niemeyer 的回答中的方法很好,如果Items 在申请期间有效。但如果不是,则可能会导致内存泄漏,因为Item的计算 observable 会从ViewModel. 再说一次,如果您永远不会摆脱任何Item对象,那也没关系。但是,如果您确实尝试摆脱Items,它们将不会被垃圾收集,因为淘汰赛仍将具有反向依赖引用。

You couldmake sure to dispose() of the computed, maybe in a cleanup() method on Itemthat gets called when the item goes away, but you have to remember to do that whenever removing Items.

可以确保计算的 dispose(),也许在Item项目消失时调用的 cleanup() 方法中,但您必须记住在删除Items时执行此操作。

Instead, why not make Itema little less smart and have ViewModeltell it when it is selected? Just make Item's isSelected()a regular old observable and then in ViewModelsubscribe to selectedItemand update inside that subscription.

相反,为什么不让它Item变得不那么聪明,并ViewModel在它被选中时告诉它?只需 makeItemisSelected()一个常规的旧可观察对象,然后在该订阅中ViewModel订阅selectedItem和更新。

Or, use @RP Niemeyer's pub/sub solution. (To be fair, this solution came about after his answer here.) You'll still need to clean up, though, because it creates reverse dependencies, too. But at least there's less coupling.

或者,使用 @RP Niemeyer 的pub/sub 解决方案。(公平地说,这个解决方案是在他在这里回答之后提出的。)但是,您仍然需要清理,因为它也会创建反向依赖关系。但至少耦合少了。

See the answer to my recent questionon this same topic for more details.

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

回答by Peter

Use $contextinstead of $parentwhen within a foreach binding.

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