如何在 Knockout 中创建计算的可观察数组
Posted
技术标签:
【中文标题】如何在 Knockout 中创建计算的可观察数组【英文标题】:How to create a computed observable array in Knockout 【发布时间】:2012-07-03 04:06:09 【问题描述】:我想知道如何创建一个计算的可观察数组。
在我的视图模型中,我有 2 个可观察数组,我想要一个计算的可观察数组,它只是两个数组的组合。
function ViewModel()
var self = this;
self.listA= ko.observableArray([]);
self.listB = ko.observableArray([]);
self.masterList= //combine both list A and B
【问题讨论】:
【参考方案1】:这将组合两个数组并返回组合列表。但是,它不是一个计算的可观察数组(不知道这是否可能),而是一个常规的计算可观察数组。
self.masterList = ko.computed(function()
return this.listA().concat(this.listB());
, this);
【讨论】:
我相信这个答案对于大多数用例来说都是有缺陷的:计算出的 observable 的值是一个常规数组,而不是一个 observable 数组(大致在答案中说明)。因此,更新listA
或listB
将完全替换数组本身,而不是更新其内容(这是我们在 99% 的情况下想要的)。 这意味着你不应该将视图绑定到这个 observable。 实际上,这个代码和它的非计算变体一样有用。查看不同方法的其他答案。
在这种情况下它不起作用,但是淘汰插件knockout-projections 使用新的array change subscriptions 实现了更高效的计算可观察数组。该插件可以扩展以支持高效的 concat 操作。
当你使用这样的策略时 self.masterList.push('element') 表示列表未定义。当您使用 ko sortable 等依赖项时,这一点变得至关重要【参考方案2】:
self.masterList = ko.observableArray();
ko.computed(function ()
self.masterList(self.listA().concat(self.listB()));
);
在精神上类似于 Joe Flateau 的回答,但我认为这种方法更简单。
【讨论】:
这也是我打算这样做的方式,但是作为公认的答案,这是否仍然受到问题的影响?因为任何更改都会导致绑定到masterList
的任何视图被完全重绘?
@AdamLewis:是的,这确实重建了整个数组,并且取决于视图引擎,它可能会或可能不会为绑定到它的任何视图重新渲染整个 DOM 子图(但不一定,可能只是做一个差异并应用它)。请注意,它可能仍然是避免许多更新的最佳解决方案。这不是我概述的关于你提到的答案的问题,如果视图引擎捕获数组属性本身(而不是它的路径),那么它永远不会检测到你交换了数组(因为它是不可观察的)和因此永远不会更新。
@tne 我现在正在使用这个答案,但它似乎真的很慢......也许我做错了什么。但是,这个解决方案是否破坏了 KO 的预期目的?还有另一种方法可以解决需要计算的可观察数组的问题吗?只是想知道这是否是在我(或任何)情况下使用的正确工具。
@Nate 只要您确实需要按特定顺序组合列表,这种方法就没有问题。我在这里只使用了concat
,因为其他人都是,以便与其他答案进行比较。如果我要删除该要求,我可能会做不同的事情(因为确实,这很慢)。例如。 push/splice 元素直接。如果元素经常更改,那么我可能会使用链表而不是数组。也许您可以根据您的要求提出一个单独的问题。【参考方案3】:
我知道这是一个老问题,但我想我会把我的答案扔在那里:
var u = ko.utils.unwrapObservable;
ko.observableArray.fn.filter = function (predicate)
var target = this;
var computed = ko.computed(function ()
return ko.utils.arrayFilter(target(), predicate);
);
var observableArray = new ko.observableArray(u(computed));
computed.subscribe(function (newValue) observableArray(newValue); );
return observableArray;
;
【讨论】:
【参考方案4】:observableArray 只是一个带有更多属性的 observable。因此,在闭包中返回数组的计算 observable 将被视为数组。
【讨论】:
嗯,有点。我刚刚对其进行了测试,似乎除非将其声明为可观察数组,否则不会为您分解出 shift 和 pop 等方法。 为了像我这样的人以后发现这一点的好处:这不是真的,请参阅接受答案中的 cmets 了解原因【参考方案5】:我不确定这是否是最有效的选择 - 但它相当简单并且适合我。 ko.computed 返回一个 observable 数组,如下所示:
self.computedArrayValue = ko.computed(function()
var all = ko.observableArray([]);
....
return all();
);
代码的工作示例: html:
<div data-bind="foreach: days">
<button class="btn btn-default btn-lg day" data-bind="text: $data, click: $root.dayPressed"></button>
</div>
视图模型上的javascript函数:
self.days = ko.computed(function()
var all = ko.observableArray([]);
var month = self.selectedMonth(); //observable
var year = self.selectedYear(); //observable
for (var i = 1; i < 29; i++)
all.push(i);
if (month == "Feb" && year % 4 == 0)
all.push(29);
else if (["Jan","Mar","May","Jul","Aug","Oct","Dec"].find((p) => p == month))
[29,30,31].forEach((i) => all.push(i));
else if (month != "Feb")
[29,30].forEach((i) => all.push(i));
return all();
);
【讨论】:
以上是关于如何在 Knockout 中创建计算的可观察数组的主要内容,如果未能解决你的问题,请参考以下文章
当使用knockout.js和Sammy.js时,如何设置内容来自模板的可观察路径
Knockout.js:当可观察数组中的值发生变化时触发计算的最佳方法是啥