Принудительно запустить вычисляемую функцию свойства


учитывая вычисленное свойство

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() дважды для возврата различных значений.

5 71

5 ответов:

Я понял, что мой первый ответ пропустил вашу точку зрения и не решит вашу проблему.

проблема в том, что вычисленный будет переоценивать только если есть некоторые наблюдаемые, которые заставляют его переоценивать. Нет никакого собственного способа заставить вычисленный повторно оценить.

однако вы можете обойти это с помощью некоторого хакерства, создав фиктивное наблюдаемое значение, а затем сообщив своим подписчикам, что оно изменилось.

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

}());​

здесь-это скрипка показывает такой подход

здесь метод чтобы заставить пересчет всех наблюдаемых в зависимости от вашего:

getCurrentValues.valueHasMutated()

этот ответ концептуально совпадает с тем, который дал @josh, но представлен как более общая оболочка. Примечание: эта версия предназначена для записи вычисляемых.

я использую Typescript, поэтому я включил ts.D определение первого. Поэтому игнорируйте эту первую часть, если она не имеет к вам отношения.

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


уведомления-записи-компьютерная

обертка для записи observable это всегда вызывает абонента уведомление-даже если никакие наблюдаемые не были обновлены в результате write вызов

просто заменить function<T> (options: KnockoutComputedDefine<T>, context) С function(options, context) если вы не используете 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);
}

основной вариант использования для этого - когда вы обновляете что-то, что в противном случае не вызвало бы изменения в наблюдаемом, которое "посещается".

например, я использую LocalStorage для установки некоторых значений, но нет никаких изменений для любого наблюдаемого триггера переоценка.

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

обратите внимание, что все, что мне нужно изменить в ko.computed до ko.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();

из комментария @mbesthttps://github.com/knockout/knockout/issues/1019.

предположим, что getCurrentValues () может возвращать различные наборы наблюдаемых которые изменяются в другом месте кода

Я предполагаю, что getCurrentValues () является функцией. Если бы вы могли сделать его вычисленным, ваш checkedValueCount просто волшебным образом начнет работать.

вы можете сделать getCurrentValues быть вычисляемым, а не функция?

поскольку нет прямого способа принудительно обновить вычисленный, я создал наблюдаемый с именем toForceComputedUpdate, и я вызвал его в вычисляемой функции, чтобы вычисленный прослушивал его обновление, а затем для принудительного обновления я называю его так toForceComputedUpdate(Math.случайная)