Заставить вычисленную функцию свойства запустить

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

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

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

Мне нужно checkedValueCount обновляться всякий раз, когда

  • изменяется одна из его зависимостей
  • getCurrentValues ​​() возвращает другой набор наблюдаемых.

Проблема заключается в том, что ko.computed, кажется, memoize последнее возвращаемое значение и обновляется только при обновлении зависимостей. Это обрабатывает первый случай, но не последний.

То, что я ищу, - это способ принудительного запуска checkedValueCount для повторного запуска. Что-то, что я могу использовать, например:

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

Проще говоря, учитывая, что у меня

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

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

Ответ 1

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

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

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

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

}());​

Вот сценарий, показывающий этот подход

Ответ 2

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

getCurrentValues.valueHasMutated()

Ответ 3

Этот ответ концептуально такой же, как и один @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);
}

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

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

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

Обратите внимание, что все, что мне нужно было изменить, было ko.computed до ko.notifyingWritableComputed, а затем все заботится о себе.

Когда я вызываю hasUserClickedFooButton(true), то наблюдаемый 'dummy' увеличивается, заставляя всех подписчиков (и их подписчиков) получать новое значение при обновлении значения в 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.

Ответ 4

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

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

Можете ли вы заставить getCurrentValues ​​быть вычисленным вместо функции?

Ответ 5

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