重新评估计算的Knockout,它仅取决于一个可观察的数组

时间:2014-01-07 15:01:29

标签: javascript arrays knockout.js

我的Appmodel包含一个可观察的评论数组

self.comments=ko.observableArray([]);  // Default Value is an empty array
/*
   Here comes some code to initially fill the observable array 
   with items from an JSON Response
*/

此外,我有两个计算器应该代表第一个注释和最后一个注释

self.firstComment = ko.computed(function () {
    var sorted = self.comments.sort(function (left, right) {
        return left.Id() - right.Id();
    });
    return sorted[0];
});

self.lastComment = ko.computed(function () {
    var sorted = self.comments.sort(function (left, right) {
        return left.Id() - right.Id();
    });
    return sorted[sorted.length - 1];
});

这非常适用于初始化应用程序(从服务器加载JSON,构建应用程序模型......),但是当我向数组添加注释时,计算结果无法识别数组项的数量已更改(据我所知,一个可观察的数组只是一个可观察数组属性本身的可观察数据。所以当我这样做时:

self.comments.push(aNewCommentObject);

self.lastComment仍然绑定到数组项,这是最初加载应用程序的时候。

我发现this blog post如何通过引入虚拟可观察来强制计算,但我不喜欢这种方法。为什么使用observableArray以及如何使用?

其他挑战:我想在每种情况下保持observableArray项目的排序(因为它的评论Feed应该按时间顺序排序)。我尝试用计算commentsSorted来做这个,但也有问题,当observableArray有新项时这不会更新,所以这里也是同样的问题。这就是原因,为什么我每次都在firstComment和lastComment中进行排序。

3 个答案:

答案 0 :(得分:4)

尝试解开注释以触发Knockout的依赖关系跟踪:

self.firstComment = ko.computed(function () {
    var sorted = self.comments().sort(function (left, right) {
        // -------------------^^ !
        return left.Id() - right.Id();
    });
    return sorted[0];
});

或(同样的事):

self.firstComment = ko.computed(function () {
    var sorted = ko.unwrap(self.comments).sort(function (left, right) {
        return left.Id() - right.Id();
    });
    return sorted[0];
});

当然,您可以将其抽象为单独的计算结果。

self.commentsSorted = ko.computed(function () {
    return self.comments().sort(function (left, right) {
        return left.Id() - right.Id();
    });
});

self.firstComment = ko.computed(function () {
    return ko.unwrap(self.commentsSorted)[0];
});

由于计算器缓存了它们的返回值(就像其他所有可观察的一样),您不必担心多次调用self.commentsSorted。只有当潜在的可观察数组出现机会时,它才会重新计算。

答案 1 :(得分:2)

你怎么能“在每种情况下保持observableArray项目的分类”?我的建议是不要尝试对原始数组进行排序(我将在下面更详细地描述陷阱),而是使用一个返回一个新的排序数组的计算observable:

self.commentsSorted = ko.computed(function () {
    return self.comments.slice(0).sort(function (left, right) {
        return left.Id() - right.Id();
    });
});

了解JavaScript数组sort()方法的一个重要事项是更改原始数组。 Knockout的observableArray包含许多数组函数,因此可以直接在observableArray对象上使用它们。这就是为什么你可以使用myObservableArray.sort()而不是myObservableArray().sort()。但这两者并不相同,因为Knockout以不同的方式包装数组mutator方法,大致如下:

sort: function (sortFunction) {
    this.valueWillMutate();
    var result = this.peek().sort(sortFunction);
    this.valueHasMutated();
    return result;
}

那么自动更改原始observableArray

有什么问题
  1. 很难理解发生了什么。很容易没有获得对数组的依赖,因此永远不会更新超过初始化,或者不通知更改,从而破坏其他元素获取正确的数组视图。

  2. 即使你确实正确设置了它,你现在已经创建了一个循环依赖,因为你有一些在数组更改时更新的东西,然后再次更新数组。虽然Knockout当前为计算的observable的同步更新禁用了这个(但是将来可能会改变),但如果使用手动订阅或使用throttle选项计算,它将导致无限递归。

    < / LI>

答案 2 :(得分:1)

替代解决方案,使用subscribe

self.comments = ko.observableArray([]);

self.comments.subscribe(function(comments) {
    var sorted = comments.sort(function (left, right) {
        return left.Id() - right.Id();
    });

    self.firstComment(sorted[0]);
    self.lastComment(sorted[sorted.length - 1]);

});