强制运行计算属性函数


80

给定计算属性

vm.checkedValueCount = ko.computed(function(){
  var observables = getCurrentValues();  //an array of ko.observable[]
  return _.filter(observables, function(v) { return v() }).length;
});

假设getCurrentValues()可以返回不同的可观察对象集,这些可观察对象集在代码的其他位置进行了修改(并且来自比observableArray更复杂的结构)。

我需要checkedValueCount随时更新

  • 其依赖项之一更改
  • getCurrentValues()返回一组不同的可观察值。

问题在于,它ko.computed似乎记住了最后返回的值,并且仅在依赖项更新时才更新。这处理第一种情况,但不适用于后者。

我正在寻找的是一种强制重新运行checkedValueCount的方法。我可以使用的东西:

changeCurrentValues();
vm.checkeValueCount.recalculate();

简而言之,鉴于我有

a = ko.computed(function() { return Math.random() })

如何强制a()两次调用才能返回不同的值。


请参阅我更新的“重写”答案。
乔什

Answers:


116

我意识到我的第一个答案错过了您的意思,也无法解决您的问题。

问题在于,只有在存在一些可观察到的迫使其重新评估的情况下,计算者才会重新评估。没有原生方法可以强制计算结果重新评估。

但是,您可以通过创建虚拟的可观察值并告诉其订户它已更改来解决一些黑客问题。

(function() {

    var vm = function() {
        var $this = this;

        $this.dummy = ko.observable();

        $this.curDate = ko.computed(function() {
            $this.dummy();
            return new Date();
        });

        $this.recalcCurDate = function() {
            $this.dummy.notifySubscribers();
        };        
    };

    ko.applyBindings(new vm());

}());​

这是显示这种方法的小提琴


啊,我试过了,但是我一定把它弄错了。这看起来很hacky,但很好(而且足够好,我可以扩展ko.computing以使其工作
George Mauer

一如既往,简单是最好的。
亨利·罗德里格斯

1
当您更改无法观察的大量数据并且更改后必须显示部分数据时,此功能将非常有用。
Marek Bar

1
您对notifySubscribers()的使用很不错。比我建立一个随机数并将其设置为dummy()的值要
好得多

9

一种方法可以根据您的要求强制重新计算所有可观测值:

getCurrentValues.valueHasMutated()

9
计算人员没有这种方法
George Mauer 2014年

getCurrentValues(); //an array of ko.observable[]
Rustam 2014年

哦,我懂了。您是说找到一个可观察的对象,该对象是计算对象的依赖项,并将其称为valueHasMutated方法。这与Josh的回答根本没有本质上的区别,是吗?实际上,它迫使您知道引用了什么,并且知道那些引用是可观察的(而不是计算的)。
乔治·莫尔2014年

2
值得一提的是此函数,因为它仍然是读者可以选择的选项,尽管也许有人可以更新已接受的答案,以说明该函数可以与notifySubscribers()互换的情况,以及是否有任何惩罚或优势。
肖恩·威尔逊

4

这个答案在概念上与@josh给出的答案相同,但以更通用的包装形式呈现。注意:此版本用于“可写”计算。

我正在使用Typescript,因此首先包含了ts.d定义。因此,如果与您无关,请忽略第一部分。

interface KnockoutStatic
{
    notifyingWritableComputed<T>(options: KnockoutComputedDefine<T>, context ?: any): KnockoutComputed<T>;
}

通知可写计算

一个可写的包装器observable总是使订户得到通知-即使由于write调用而没有更新可观察的对象

如果不使用Typescript,只需替换function<T> (options: KnockoutComputedDefine<T>, context)function(options, context)

ko.notifyingWritableComputed = function<T> (options: KnockoutComputedDefine<T>, context)
{
    var _notifyTrigger = ko.observable(0);
    var originalRead = options.read;
    var originalWrite = options.write;

    // intercept 'read' function provided in options
    options.read = () =>
    {
        // read the dummy observable, which if updated will 
        // force subscribers to receive the new value
        _notifyTrigger();   
        return originalRead();
    };

    // intercept 'write' function
    options.write = (v) =>
    {
        // run logic provided by user
        originalWrite(v);

        // force reevaluation of the notifyingWritableComputed
        // after we have called the original write logic
        _notifyTrigger(_notifyTrigger() + 1);
    };

    // just create computed as normal with all the standard parameters
    return ko.computed(options, context);
}

此方法的主要用例是在您更新某项内容时,否则该内容将不会触发该read函数“访问”的可观察对象的更改。

例如,我正在使用LocalStorage设置一些值,但是对任何可观察到的值均未进行更改以触发重新评估。

hasUserClickedFooButton = ko.notifyingWritableComputed(
{
    read: () => 
    {
        return LocalStorageHelper.getBoolValue('hasUserClickedFooButton');
    },
    write: (v) => 
    {
        LocalStorageHelper.setBoolValue('hasUserClickedFooButton', v);        
    }
});

请注意,所有我需要的变化是ko.computedko.notifyingWritableComputed,然后一切都需要自己照顾自己。

当我打电话时hasUserClickedFooButton(true),“虚拟”可观察值将增加,从而迫使任何订户(及其订户)在更新LocalStorage中的值时获取新值。

(注意:您可能认为notify: 'always'扩展器是这里的一个选项-但这有所不同)。


还有一个仅可读取的可计算可观察值的附加解决方案:

ko.forcibleComputed = function(readFunc, context, options) {
    var trigger = ko.observable().extend({notify:'always'}),
        target = ko.computed(function() {
            trigger();
            return readFunc.call(context);
        }, null, options);
    target.evaluateImmediate = function() {
        trigger.valueHasMutated();
    };
    return target;
};


myValue.evaluateImmediate();

来自@mbest评论https://github.com/knockout/knockout/issues/1019


你什么意思没有价值?在切换到此之前,它是否有效。您是否正在使用延迟更新(全局设置)。刚意识到,如果您希望能够更新可观察值然后立即使用该值,则可能会发生冲突。如果是这样,请尝试将ko.tasks.runEarly()添加到我的方法中。
Simon_Weaver

1

假设getCurrentValues()可以返回不同的可观察对象集,这些可观察对象集在代码中的其他地方进行了修改

我假设getCurrentValues()是一个函数。如果您可以对其进行计算,那么您的checkedValueCount就会神奇地开始工作。

您可以使getCurrentValues成为计算的而不是函数的吗?


我在很大程度上简化了我的实际情况,但是可以肯定地说(如果需要例如使用参数,情况就不会这样)-我不知道这会有什么不同。在getCurrentValues()内部,我将其他数据切片和切块,以确定需要返回树视图的哪些节点。问题是该函数可以返回不同的可观察值,而我需要计算出来的值。基本上,文档所说的是动态依赖关系,但动态添加了它们的复杂性
George Mauer

如果“切片和切块”基于可观察物,那将会有所不同。例如,假设您的切片和切块是具有.IsChecked()== true的节点。然后,您将得到一个名为.currentValues()的计算结果,该计算结果将评估所有节点,并使用.IsChecked()返回那些节点。可以对可观察的属性进行切片和切块吗?
Judah Gabriel Himango

我真的不确定您在说什么犹大,就我而言,每次调用都不是可观察到的-但更重要的是,我不认为这可能有多重要,淘汰赛并没有对任何事情产生任何影响您调用了什么闭包(我没有检查过,但是除非javascript完全进入了lisp-land,否则这是不可能的)。计算出的所有能力充其量只能跟踪任何被调用的可观察对象。
乔治·莫尔

-1

由于没有直接强制更新计算的直接方法,因此我创建了一个名为toForceComputedUpdate的可观察对象,并在计算函数中调用了它,以便计算机将侦听其更新,然后强制更新,我将其称为toForceComputedUpdate(Math 。随机)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.