有没有办法告诉knockout等到重新计算计算值,直到定义视图模型?

时间:2012-07-19 21:08:23

标签: javascript knockout.js

我有一个复杂的视图模型,它是几百行javascript代码,具有大量可观察属性,计算可观察属性,可写计算可观察属性和函数。所以管理这个是一个相当大的挑战。

我必须处理的一个恼人的问题是计算的observable会在你定义时立即计算出来。因此,在定义可观察对象时,使用尚未在视图模型中定义的变量会导致错误,表明该变量尚未定义。它......就在文件的后面。

这是一个人为的例子:

function ViewModel1​(args) {
    var self = this;

    self.firstName = ko.observable(args.firstName);
    self.lastName = ko.observable(args.lastName);
    self.fullName = ko.computed(function () {
        return self.firstName() + ' ' + self.lastName();
    });
}

function ViewModel2​(args) {
    var self = this;

    self.fullName = ko.computed(function () {
        // uh oh, where's firstName?
        return self.firstName() + ' ' + self.lastName();
    });
    self.firstName = ko.observable(args.firstName);
    self.lastName = ko.observable(args.lastName);
}

使用ViewModel1可以正常使用。在定义fullName时,firstNamelastName已定义,因此它按预期工作。使用ViewModel2将无效。计算函数中会出现一个错误,表明firstName未定义。

到目前为止,我一直在做的是确保在定义了所有因变量之后定义所有计算的可观察量。这样做的问题在于,当我宁愿将相关变量保持在一起时,事情在看似随机的地方被定义。

我提出的一个很好的解决方案是将“初始化”可观察集定义为true,并使所有计算的可观察对象测试它是否仍在初始化并计算并返回该值而不是。这样,将不会尝试访问当前未定义的变量。

function ViewModel3(args) {
    var self = this;
    var initializing = ko.observable(true);

    self.fullName = ko.computed(function () {
        if (!initializing()) {
            return self.firstName() + ' ' + self.lastName();
        }
    });
    self.firstName = ko.observable(args.firstName);
    self.lastName = ko.observable(args.lastName);

    initializing(false);
}

但是,在我的情况下,这不是很实用。我有很多计算的observable,所以在所有这些中做这个会使它非常臃肿,记住我有很多这些。限制它似乎没有什么区别。

在尝试计算计算出的观察值之前,有没有办法告诉淘汰赛等待?或者我有更好的方法来构建我的代码来处理这个问题吗?

我可能会做一些辅助函数来管理初始化逻辑,但我仍然需要改变所有计算的可观察定义。我想我可以通过猴子补丁淘汰来添加这个初始化逻辑,因为我不知道淘汰赛有这样的选择,我可能会这样做。之前我已经查看了计算可观察量的来源,但我不知道其他地方是否已有设置。

jsfiddle demo

1 个答案:

答案 0 :(得分:40)

Computed observables接受deferEvaluation选项,该选项阻止初始评估发生,直到实际尝试检索计算值的值为止。

您可以将其定义为:

self.fullName = ko.computed({
   read: function() {
       return self.firstName() + " " + self.lastName();
   },
   deferEvaluation: true
});

为了完整起见,您还可以将其指定为:

this.fullName = ko.computed(function() {
       return this.firstName() + " " + this.lastName();
 }, this, { deferEvaluation: true });

或者你可以把它包起来:

ko.deferredComputed = function(evaluatorOrOptions, target, options) {
   options = options || {};

   if (typeof evaluatorOrOptions == "object") {
       evaluatorOrOptions.deferEvaluation = true;   
   } else {
       options.deferEvaluation = true;
   }

   return ko.computed(evaluatorOrOptions, target, options);
};