【问题标题】:Force a computed property function to run强制计算属性函数运行
【发布时间】:2012-11-26 00:12:41
【问题描述】:

给定一个计算属性

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

假设 getCurrentValues() 可以返回不同的 observable 集合,这些集合在代码的其他地方被修改(并且来自比 observableArray 更复杂的结构)。

我需要checkedValueCount随时更新

  • 其中一个依赖项发生了变化
  • getCurrentValues() 返回一组不同的 observables。

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

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

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

简单地说,我有

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

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

【问题讨论】:

  • 查看我更新的“重写”答案。

标签: knockout.js computed-observable


【解决方案1】:

我意识到我的第一个答案错过了您的观点,并且不会解决您的问题。

问题是,如果有一些 observable 迫使它重新评估,那么计算只会重新评估。没有本地方法可以强制计算重新评估。

但是,您可以通过创建一个虚拟的可观察值然后告诉它的订阅者它已经改变来解决这个问题。

(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());

}());​

Here is a Fiddle 展示了这种方法

【讨论】:

  • 啊,我试过了,但我一定是接错了。这看起来很老套,但很好(而且足够好,我可以扩展 ko.computed 以使其工作
  • 一如既往,越简单越好。
  • 当您更改不可观察的大型数据数组并且更改后必须显示某些数据部分时,这很有帮助。
  • 您对 notifySubscribers() 的使用很好。比我构建一个随机数并将其设置为 dummy() 的值更好
  • notifySubscribers() 是我想要的。谢谢!
【解决方案2】:

a method 可以根据你的情况强制重新计算所有 observables:

getCurrentValues.valueHasMutated()

【讨论】:

  • 计算没有这个方法
  • getCurrentValues(); //an array of ko.observable[]
  • 哦,我明白了。你是说找到一个依赖于计算的可观察对象并将其称为valueHasMutated 方法。这与上面乔希的回答并没有本质上的不同,是吗?事实上,它迫使您知道引用了什么,并知道这些引用是可观察的(而不是计算的)。
  • 尽管如此,还是提一下这个函数还是不错的,因为它仍然是读者的一个选项,尽管也许有人可以更新已接受的答案以说明此函数可以与 notifySubscribers() 互换的情况,如果有任何无论是惩罚还是优势。
【解决方案3】:

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

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

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

通知可写计算

一个可写的observable 的包装器,总是导致订阅者收到通知 - 即使没有任何 observable 因write 调用而更新

如果您不使用 Typescript,只需将 function&lt;T&gt; (options: KnockoutComputedDefine&lt;T&gt;, 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 函数“访问”的 observable 更改的内容时。

例如,我正在使用 LocalStorage 设置一些值,但没有任何可观察到的变化来触发重新评估。

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

请注意,我只需要将ko.computed 更改为ko.notifyingWritableComputed,然后一切都会自行处理。

当我调用 hasUserClickedFooButton(true) 时,'dummy' observable 会递增,强制任何订阅者(及其订阅者)在 LocalStorage 中的值更新时获取新值。

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


对于只可读的计算 observable 有一个额外的解决方案:

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

【讨论】:

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

假设 getCurrentValues() 可以返回不同的 observables 集 在代码的其他地方进行了修改

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

你能让 getCurrentValues 成为一个计算值而不是一个函数吗?

【讨论】:

  • 我过分简化了我的实际场景,但可以肯定的是,假设它可以(例如,如果需要获取参数,则情况并非如此) - 我不明白那会如何有所作为。在 getCurrentValues() 内部,我正在对其他数据进行切片和切块,以确定需要返回树视图的哪些节点。问题是该函数可以返回不同的可观察值,我需要计算来获取它。基本上正是文档所讨论的动态依赖关系,但它们的复杂性是动态添加的
  • 如果“切片和切块”是基于可观察的,那将会有所不同。例如,假设您的切片和切块是具有 .IsChecked() == true 的节点。然后,您将有一个名为 .currentValues() 的计算值,它将评估所有节点并返回带有 .IsChecked() 的节点。可以对可观察的属性进行切片和切块吗?
  • 我真的不确定你在说什么犹大,在我的情况下,每一次调用都不能成为可观察到的 ko - 但更重要的是,我看不出这有什么关系,淘汰赛不会对您调用的闭包进行任何反思(我没有检查过,但除非 javascript 完全进入 lisp-land,否则这是不可能的)。所有计算最多只能跟踪任何被调用的可观察对象。
【解决方案5】:

由于没有直接的方法来强制更新计算,我创建了一个名为 toForceComputedUpdate 的 observable,我在计算函数中调用它,因此计算将监听它的更新,然后强制更新我这样称呼它toForceComputedUpdate(Math.random)

【讨论】:

    猜你喜欢
    • 2020-12-10
    • 1970-01-01
    • 1970-01-01
    • 2014-06-17
    • 2021-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-23
    相关资源
    最近更新 更多