Заставить вычисленную функцию свойства запустить
Учитывая вычисленное свойство
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)