Computed Observables in Knockout are a useful way of calculating values based on other observables:
var ExampleViewModel = function() {
this.name = ko.observable();
this.isNameValid = ko.computed(function() {
return this.name().length > 0;
});
};
One of the great features here is that when the name
property changes the Knockout framework will automatically re-evaluate the computed observable and notify anything that is bound to that value. This works by automatically subscribing to the change notifications from the name
observable when the value if first acquired.
Unfortunately this does not work when the value of the computed observable is dependent on a non-observable value:
var ExampleViewModel = function(externalValidationDefinition) {
this.name = ko.observable();
this.isNameValid = ko.computed(function() {
return this.name().length > externalValidationDefinition.minLength;
});
};
var validationDefinition = {
minLength: 5;
};
var vm = new ExampleViewModel(validationDefinition);
//change the validation definition...
validationDefinition.minLength = 10;
//...but the computed observable is not re-evaluated
In this situation we need to manually force the re-evaluation of the computed observable, but there is no native way of doing so.
To get around this we can add a dummy observable to the view model, then retrieve and ignore its value from within the computed observable. This will cause the Knockout framework to re-evaluate the computed observable whenever the dummy observable changes:
var ExampleViewModel = function(externalValidationDefinition) {
var _dummyObservable = ko.observable();
this.name = ko.observable();
this.isNameValid = ko.computed(function() {
_dummyObservable(); //retrieve and ignore the value
return this.name().length > externalValidationDefinition.minLength;
});
};
Now we can invalidate the computed observable just by invalidating the value of the dummy one:
var ExampleViewModel = function(externalValidationDefinition) {
var _dummyObservable = ko.observable();
this.name = ko.observable();
this.isNameValid = ko.computed(function() {
_dummyObservable(); //retrieve and ignore the value
return this.name().length > externalValidationDefinition.minLength;
});
this.invalidateIsNameValid = function() {
_dummyObservable.notifySubscribers(); //fake a change notification
};
};
var validationDefinition = {
minLength: 5;
};
var vm = new ExampleViewModel(validationDefinition);
//change the validation definition...
validationDefinition.minLength = 10;
//...and manually invalidate
vm.invalidateIsNameValid();
Now we can manually force the computed observable to be updated when we know that an external factor has changed. If I get the time I may convert this into an extender or helper for reusability later…