简体   繁体   English

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

[英]Reevaluating a Knockout computed which depends just on an observable array

My Appmodel consists of an observable array of comments 我的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
*/

Furthermore I have two computeds which should represent the very first comment and the last comment 此外,我有两个计算器应该代表第一个评论和最后一个评论

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

This works perfectly on initializing the application (loading the JSON from Server, build up App model...), but when I add a comment to the array, the computeds do not recognize that the number of array items has changed (as I understood it, an observable array is just an observable where the array properties themselves are observed). 这完全适用于初始化应用程序(从服务器加载JSON,构建应用程序模型......),但是当我向数组添加注释时,计算器不能识别数组项的数量已经改变(据我了解)它,一个可观察的数组只是一个可观察数组属性本身的可观察数据。 So when I do: 所以当我这样做时:

self.comments.push(aNewCommentObject);

self.lastComment is still bound to the array item, that it was when the app loaded initially. self.lastComment仍然绑定到数组项,这是最初加载应用程序时的情况。

I have found this blog post how to force computation by introducing a dummy observable, but I don't like the approach. 我发现这个博客文章如何通过引入虚拟可观察量来强制计算,但我不喜欢这种方法。 For what purpose is an observableArray used then and how? 出于什么目的,使用了observableArray以及如何使用?

Additional Challenge: I would like to keep the observableArray Items sorted under every circumstance (because its a comment feed which should be just sorted chronologically). 附加挑战:我想在每种情况下保持observableArray项目的排序(因为它的评论提要应该按时间顺序排序)。 I tried to do this whith an computed commentsSorted but also have problems that this does not update when the observableArray has new items, so same problem here. 我尝试用计算的commentsSorted做这个,但也有问题,当observableArray有新项时,这不会更新,所以这里也是同样的问题。 Thats the reason, why I am sorting everytime in firstComment and lastComment. 这就是原因,为什么我每次都在firstComment和lastComment中进行排序。

Try unwrapping the comments to trigger Knockout's dependency tracking: 尝试解开注释以触发Knockout的依赖关系跟踪:

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

or (same thing): 或(同样的事):

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

Of course you can abstract this into a separate computed. 当然,您可以将其抽象为单独的计算机。

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

Since computeds cache their return values (just like every other observable does) you don't need to worry about calling self.commentsSorted multiple times. 由于计算机缓存了它们的返回值(就像其他所有可观察的一样),您不必担心多次调用self.commentsSorted It will re-calculate only when the underlying observable array chances. 只有当潜在的可观察数组出现机会时,它才会重新计算。

How can you "keep the observableArray items sorted under every circumstance"? 你怎么能“在每种情况下保持observableArray项目的分类”? My advice is to not try to sort the original array (I'll describe the pitfalls in more detail below) but to use a computed observable that returns a new, sorted array: 我的建议是不要尝试对原始数组进行排序(我将在下面更详细地描述陷阱),但要使用一个返回一个新的排序数组的计算observable:

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

An important thing to understand about the JavaScript Array sort() method is that it changes the original array . 了解JavaScript Array sort()方法的一个重要方面是它更改了原始数组 Knockout's observableArray wraps many of the array functions so they can be used directly on the observableArray object. Knockout的observableArray包含许多数组函数,因此可以直接在observableArray对象上使用它们。 That's why you can use myObservableArray.sort() instead of having to do myObservableArray().sort() . 这就是为什么你可以使用myObservableArray.sort()而不是必须做myObservableArray().sort() But the two aren't equivalent because Knockout wraps array mutator methods differently, approximately like this: 但是这两个并不相同,因为Knockout以不同的方式包装数组mutator方法,大致如下:

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

So what's wrong with automatically changing the original observableArray ? 那么自动更改原始observableArray什么问题?

  1. It's hard to grasp what's going on. 很难理解发生了什么。 It's easy to either not get a dependency on the array and thus never update past the initialization, or to not notify of the change and thus break other elements from getting the correct view of the array. 很容易没有得到对数组的依赖,因此永远不会更新超过初始化,或者不通知更改,从而破坏其他元素获取正确的数组视图。

  2. Even if you do set it up correctly, you've now created a circular dependency because you have something that's updated whenever the array changes, which then updates the array again. 即使你确实正确设置了它,你现在已经创建了一个循环依赖,因为你有一些在数组更改时更新的东西,然后再次更新数组。 Although Knockout currently disables this for synchronous updates of a computed observable (but that might change in the future), it will result in unbounded recursion if using a manual subscription or a computed with the throttle option. 虽然Knockout当前为计算的observable的同步更新禁用了这个(但是将来可能会改变),但如果使用手动订阅或使用throttle选项计算,它将导致无限递归。

Alternative solution, using subscribe : 替代解决方案,使用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]);

});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM