Knockout.js 如何在Knockout中创建计算的可观察数组

Knockout.js 如何在Knockout中创建计算的可观察数组,knockout.js,ko.observablearray,computed-observable,Knockout.js,Ko.observablearray,Computed Observable,我想知道如何创建一个计算的可观察数组 在我的视图模型中,我有两个可观察数组,我希望有一个计算的可观察数组,它是两个数组的简单组合 function ViewModel() { var self = this; self.listA= ko.observableArray([]); self.listB = ko.observableArray([]); self.masterList= //combine both list A and B 这将组合两个数组并返

我想知道如何创建一个计算的可观察数组

在我的视图模型中,我有两个可观察数组,我希望有一个计算的可观察数组,它是两个数组的简单组合

function ViewModel() {
    var self = this;
    self.listA= ko.observableArray([]);
    self.listB = ko.observableArray([]);
    self.masterList= //combine both list A and B

这将组合两个数组并返回组合列表。然而,它不是一个计算的可观察数组(不知道这是否可能),而是一个常规的计算可观察数组

self.masterList = ko.computed(function() {
    return this.listA().concat(this.listB());
}, this);

observableArray只是一个具有更多属性的可观察对象。因此,在闭包中返回数组的计算可观测值将被视为数组。

我知道这是一个老问题,但我想我会在这里给出我的答案:

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

与Joe Flateau的回答在精神上类似,但我喜欢认为这种方法更简单。

我不确定这是否是最有效的选择-但它相当简单,对我来说很有效。ko.computed返回一个可观察数组,如下所示:

self.computedArrayValue = ko.computed(function() {
    var all = ko.observableArray([]);
    ....
    return all(); 
});
代码的一个工作示例: Html:


视图模型上的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(); 
});
self.days=ko.computed(函数(){
var all=可观察的高水平([]);
var month=self.selectedMonth();//可观察
var year=self.selectedYear();//可观察
对于(变量i=1;i<29;i++){
全部。推(i);
}
如果(月份=“二月”&&4年==0){
全部。推(29);
}else if([“1月”、“3月”、“5月”、“7月”、“8月”、“10月”、“12月])查找((p)=>p==month)){
[29,30,31].forEach((i)=>all.push(i));
}否则,如果(月!=“二月”){
[29,30].forEach((i)=>all.push(i));
}
返回全部();
});

嗯,算是吧。我刚刚测试了它,似乎除非它被声明为一个可观察的数组,否则像shift和pop这样的方法不会为你而出现。为了像我这样的人的利益,我后来发现:这不是真的,关于为什么我认为这个答案在大多数用例中是有缺陷的,请参见接受答案上的注释:计算出的可观测值是一个规则数组,而不是一个可观测数组(答案中大致说明)。因此,更新
listA
listB
将完全替换数组本身,而不是更新其内容(在99%的情况下,这是我们想要的)。这意味着您不应该将视图绑定到此可观察对象。实际上,此代码与其非计算变量一样有用。查看其他不同方法的答案。在这种情况下,它不起作用,但是敲除插件使用newish实现了更高效的计算可观察数组。该插件可以扩展以支持有效的concat操作。当您使用这种策略时,self.masterList.push('element')表示列表未定义。当您使用像ko sortable这样的依赖项时,这一点变得至关重要。我也打算这样做,但作为公认的答案,这个问题是否仍然存在问题;任何更改都会导致绑定到
主列表的任何视图被完全重新绘制?@AdamLewis:是的,这确实会重建整个数组,并且根据视图引擎的不同,它可能会也可能不会为绑定到它的任何视图重新渲染整个DOM子图(不一定,可能只是做一个diff并应用它)。请注意,它可能仍然是避免许多更新的最佳解决方案。但这不是我针对您提到的答案所概述的问题,如果视图引擎捕获数组属性本身(与它的路径相反),那么它将永远不会检测到您交换了数组(因为它不可观察),因此将永远不会更新。@t我现在使用这个答案,但它看起来非常慢。。。也许我做错了什么。然而,这种解决方案是否违背了KO的预期目的?有没有其他方法可以解决需要计算可观测阵列的问题?只是想知道这是否是在我(或任何人)的情况下使用的正确工具。@Nate只要您确实需要按此特定顺序组合列表,这种方法就没有问题。我在这里只使用了
concat
,因为其他人很容易与其他答案进行比较。如果我删除了这个要求,我可能会做不同的事情(因为这确实很慢)。例如,直接使用元素。如果元素经常更改,那么我可能会使用链表而不是数组。也许你可以根据自己的要求提出一个单独的问题。
<div data-bind="foreach: days">
    <button class="btn btn-default btn-lg day" data-bind="text: $data, click: $root.dayPressed"></button>        
</div> 
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(); 
});