如何从 .compulated() 可观察对象内部使用淘汰赛的 $parent/$root 伪变量?
在 knockout.js 绑定表达式中,我可以使用 $data
、$parent
和 $root
伪变量。当我使用 ko.compulated
observable 时,如何获得这些伪变量的等价物在 JavaScript 中声明?
我有一个带有子集合的父视图模型,并且父视图模型有一个 selectedChild
observable。鉴于此,我可以使用数据绑定表达式将 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,因为淘汰可能会定义这些变量,并以某种方式让它们在作用域内。调用我的计算
评估器函数:
{
name: 'Bob',
isSelected: ko.computed(function(){ return $data === $root.selectedChild(); })
}
但没有这样的运气:在我的评估器函数
内部,$data
和$root
是未定义
。
我还尝试在评估器中使用 ko.contextFor
,因为它确实授予对 $data
和 $root
的访问权限。不幸的是,在我的求值器函数中,contextFor
也始终返回undefined
。 (无论如何,我并没有对这个策略抱有很高的希望——如果我必须像这样在背后进行,目前还不清楚淘汰赛能够如何很好地跟踪依赖关系。)
我总是可以手动为每个孩子设置一个属性引用回父视图模型的视图模型。但我知道淘汰赛有能力为我做到这一点,并且我想至少在我自己编写之前探索一下我是否可以使用它的机制。
看起来应该可以将上面的绑定表达式转换为计算的可观察量 - 毕竟,这就是淘汰赛已经做的事情:
另一个巧妙的技巧是声明性绑定可以简单地实现为计算的可观察量。
但是,当我编写自己的计算可观察量时,如何处理 $data
和 $root
伪变量呢?
Inside a knockout.js binding expression, I can use the $data
, $parent
, and $root
pseudovariables. How can I get the equivalent of those pseudovariables when I'm using a ko.computed
observable declared in JavaScript?
I've got a parent viewmodel with a collection of children, and the parent viewmodel has a selectedChild
observable. Given that, I can use databinding expressions to add a CSS class to whichever child is currently selected:
<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 isSelected
computed property on the child viewmodel, so I can then add other computed properties that depend on it.
I've tried just writing JavaScript that refers to $data
and $root
, on the off-chance that knockout might define those variables and somehow have them be in scope when it calls my computed
evaluator function:
{
name: 'Bob',
isSelected: ko.computed(function(){ return $data === $root.selectedChild(); })
}
But no such luck: inside my evaluator function
, both $data
and $root
are undefined
.
I've also tried using ko.contextFor
inside my evaluator, since it does give access to $data
and $root
. Unfortunately, inside my evaluator function, contextFor
also 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.)
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:
The other neat trick is that declarative bindings are simply implemented as computed observables.
But how do I go about dealing with the $data
and $root
pseudovariables when I'm writing my own computed observable?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
伪变量仅在数据绑定上下文中可用。理想情况下,视图模型本身不应该知道或对显示它的视图有任何依赖性。
因此,当在视图模型中添加计算的可观察量时,您不知道它将如何绑定(就像 $root 一样)。视图模型或视图模型的一部分甚至可以分别绑定到不同级别的页面的多个区域,因此伪变量会根据您开始使用的元素而有所不同。
这取决于您想要完成的任务,但是如果您希望您的孩子有一个
isSelected
计算可观察量来指示该项目是否与父视图模型上的选定项目相同,那么您将需要找到一种方法让父母可以为孩子服务。一种选择是将父级传递给子级的构造函数。您甚至不需要将指向父级的指针添加为子级的属性,只需在计算的可观察值中直接使用它即可。
类似:
示例:http://jsfiddle.net/rniemeyer/BuH7N/
如果您关心的是所选状态,然后您可以调整它以将对
selectedItem
可观察到的引用传递给子构造函数,例如: http://jsfiddle.net/rniemeyer/R5MtC/如果您的父视图模型存储在全局变量中,那么您可以考虑不将其传递给孩子并直接使用它,例如: http://jsfiddle.net/rniemeyer/3drUL/。不过,我更喜欢将参考信息传递给孩子。
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.
It depends on what you are trying to accomplish, but if you want your child to have an
isSelected
computed 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.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.
Something like:
Sample here: 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
selectedItem
observable to the child constructor like: 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.
根据我的经验,如果
Item
在应用程序的持续时间内处于活动状态,@RP Niemeyer 的答案中的方法就很好。但如果不是,它可能会导致内存泄漏,因为Item
的计算可观察量设置了与ViewModel
的反向依赖关系。再说一遍,如果您从未删除任何Item
对象,那也没关系。但是,如果您确实尝试摆脱Item
,它们将不会被垃圾收集,因为淘汰仍将具有反向依赖引用。您可以确保对计算结果进行 dispose() 处理,也许是在 Item 消失时调用的 cleanup() 方法中,但您必须记住每当删除
Item
时都执行此操作。相反,为什么不让
Item
变得不那么智能,并让ViewModel
告诉它何时被选择呢?只需将Item
的isSelected()
设为常规的旧可观察对象,然后在ViewModel
中订阅selectedItem
并在内部更新那个订阅。或者,使用 @RP Niemeyer 的 pub/sub 解决方案 。 (公平地说,这个解决方案是在他在这里回答之后出现的。)不过,您仍然需要清理,因为它也会创建反向依赖关系。但至少耦合性减少了。
有关更多详细信息,请参阅关于同一主题的我最近的问题的答案。
In my experience the approach in @RP Niemeyer's answer is fine if
Item
s live for the duration of the application. But if not, it can lead to memory leaks, becauseItem
's computed observable sets up a reverse dependency from theViewModel
. Again, that's ok if you never get rid of anyItem
objects. But if you do try to get rid ofItem
s they won't get garbage collected because knockout will still have that reverse dependency reference.You could make sure to dispose() of the computed, maybe in a cleanup() method on
Item
that gets called when the item goes away, but you have to remember to do that whenever removingItem
s.Instead, why not make
Item
a little less smart and haveViewModel
tell it when it is selected? Just makeItem
'sisSelected()
a regular old observable and then inViewModel
subscribe toselectedItem
and update inside that subscription.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.
See the answer to my recent question on this same topic for more details.