问题
I'm faced a problem that my computed observable stops triggering after some sequence of dependency changes. Finally I found out the point: if dependency was inside of false branch statement during latest evaluation, computed will not be triggered next time even if condition became true before evaluation finished. Here is a sample: https://jsfiddle.net/sgs218w0/1/
var viewModel = new function(){
var self = this;
self.trigger = ko.observable(true);
self.fire = function(){
self.trigger(! self.trigger());
};
self.content = function(){
var test = 3;
return ko.computed(function(){
alert("triggered!");
if(test !== 0){
console.log(self.trigger());
alert(test);
}
test--;
});
}();
};
ko.applyBindings(viewModel);
Is it bug or feature? Do you know any workaround for this issue? I seems to be optimization, but it looks aggressive and incorrect for me. (Edit: I changed my mind. It is reasonable, but can lead to some issues sometimes. I think knockout should have options to fix this issues)
P.S. I could publish more detailed example of real code to make question more specific, if you need it. But the point of real code it the same.
UPDATE Well, I had to be less lazy to provide more detailed example of what I want achieve. I like the idea of computed which automatically make ajax calls. Described here. One disadventure I see is that call will be made even if corresponding part of UI is invisible. I tried to fix it this way: https://jsfiddle.net/bpr88bp3/1/. The problem is that once tab is deativated it can't be activated anymore, because computed stops triggering...
回答1:
After reading the update to your question and looking through the updated example code, I've come up with a real solution. This uses a pureComputed
to do the update, taking advantage of the fact that a pure computed can be activated and deactivated by subscribing to it and disposing the subscription. Here is the important code:
updateComputed = ko.pureComputed(function () {
updateTrigger();
result(evaluator.call(owner));
});
ko.computed(function () {
var isActive = result.active();
if (isActive && !updateSubscription) {
updateSubscription = updateComputed.subscribe(function () {});
} else if (updateSubscription && !isActive) {
updateSubscription.dispose();
updateSubscription = undefined;
}
});
https://jsfiddle.net/mbest/bpr88bp3/2/
回答2:
According to the Knockout JS documentation:
So, Knockout doesn’t just detect dependencies the first time the evaluator runs - it redetects them every time.
When if(test !== 0){
is false, Knockout does not subscribe to self.trigger()
due to self.trigger()
not being called during computed recalculation.
With no subscription to the self.trigger()
, there is no recalculation of the computed on further self.trigger()
changes.
IMHO the workaround is to get self.trigger()
in any case (updated fiddle):
self.content = function(){
var test = 3;
return ko.computed(function(){
var triggerValue = self.trigger();
alert("triggered!");
if(test !== 0){
console.log(triggerValue);
alert(test);
}
test--;
});
}();
回答3:
The idea, in general, is that all dependencies of a computed observable should also be observables. In your case, test
isn't an observable; if you make it observable, your code should work as expected:
self.content = function(){
var test = ko.obseravble(3);
return ko.computed(function(){
alert("triggered!");
if(test() !== 0){
console.log(self.trigger());
alert(test);
}
test(test()-1);
});
}();
But this introduces another problem because you're now changing one of the dependencies within your computed observable. By changing test
, logically, the computed should be evaluated recursively. If the computed is synchronous, Knockout prevents this, but this is not really a feature, and it could cause problems later.
Here's what I think is a better solution that properly isolates the meanings of the components:
self.content = function(){
var respondToTriggerChange = ko.obseravble(3);
self.trigger.subscribe(function () {
respondToTriggerChange(respondToTriggerChange()-1);
});
return ko.computed(function(){
alert("triggered!");
if(respondToTriggerChange()){
console.log(self.trigger());
alert(test);
}
});
}();
来源:https://stackoverflow.com/questions/36036356/computed-stops-triggering-forever-if-dependency-is-inside-of-false-branch-statem