如何在 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 数组(大致在答案中说明)。因此,更新listAlistB 将完全替换数组本身,而不是更新其内容(这是我们在 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 中创建计算的可观察数组的主要内容,如果未能解决你的问题,请参考以下文章

我可以得到我正在 foreach 的可观察数组吗?

当使用knockout.js和Sammy.js时,如何设置内容来自模板的可观察路径

Knockout.js:当可观察数组中的值发生变化时触发计算的最佳方法是啥

Angular 2:如何在组件将观察更改的服务中创建一个数组?

从数据数组中创建observable并将所有这些链接在一起

如何在 Knockout.js 中嵌套对象