Force a computed property function to run

后端 未结 5 1541
鱼传尺愫
鱼传尺愫 2021-01-30 15:29

Given a computed property

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


        
相关标签:
5条回答
  • 2021-01-30 16:04

    I realized my first answer missed your point, and won't solve your issue.

    The problem is that a computed will only reevaluate if there is some observable that forces it to re-evaluate. There is no native way to force a computed to re-evaluate.

    However, you can get around this with some hackery by creating a dummy observable value and then telling its subscribers that it has changed.

    (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 showing this approach

    0 讨论(0)
  • 2021-01-30 16:11

    There is a method to force recalculation of all observables depending on yours:

    getCurrentValues.valueHasMutated()
    
    0 讨论(0)
  • 2021-01-30 16:21

    since there is no straight forward way to force update a computed, i have created an observable named toForceComputedUpdate, and i called it within the computed function so the computed will listen to its update, then to force update i call it like this toForceComputedUpdate(Math.random)

    0 讨论(0)
  • 2021-01-30 16:26

    This answer is conceptually the same as the one @josh gave, but presented as a more generic wrapper. Note: this version is for a 'writeable' computed.

    I'm using Typescript so I've included the ts.d definition first. So ignore this first part if not relevant to you.

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

    Notifying-writeable-computed

    A wrapper for a writable observable that always causes subscribers to be notified - even if no observables were updated as a result of the write call

    Just replace function<T> (options: KnockoutComputedDefine<T>, context) with function(options, context) if you don't use Typescript.

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

    The main use case for this is when you are updating something that would not otherwise trigger a change in an observable that is 'visited' by the read function.

    For instance I am using LocalStorage to set some values, but there is no change to any observable to trigger re-evaluation.

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

    Note that all I needed to change was ko.computed to ko.notifyingWritableComputed and then everything takes care of itself.

    When I call hasUserClickedFooButton(true) then the 'dummy' observable is incremented forcing any subscribers (and their subscribers) to get the new value when the value in LocalStorage is updated.

    (Note: you may think the notify: 'always' extender is an option here - but that's something different).


    There is an additional solution for a computed observable that is only readble:

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

    From @mbest comment https://github.com/knockout/knockout/issues/1019.

    0 讨论(0)
  • 2021-01-30 16:29

    suppose getCurrentValues() can return different sets of observables which are modified elsewhere in the code

    I assume getCurrentValues() is a function. If you could make it a computed, your checkedValueCount would just magically start working.

    Can you make getCurrentValues be a computed instead of a function?

    0 讨论(0)
提交回复
热议问题