I have a complex view model which is a couple hundred lines of javascript code with a good amount of observable properties, computed observable properties, writable computed observable properties and functions. So managing this is quite a bit of a challenge.
An annoying problem that I've had to deal with is that computed observables are calculated immediately right when you define it. So using variables that haven't been defined yet in the view model at the point of defining the observable lead to errors stating that the variable hasn't been defined. It is... just later in the file.
Here's a contrived example:
function ViewModel1?(args) {
var self = this;
self.firstName = ko.observable(args.firstName);
self.lastName = ko.observable(args.lastName);
self.fullName = ko.computed(function () {
return self.firstName() + ' ' + self.lastName();
});
}
function ViewModel2?(args) {
var self = this;
self.fullName = ko.computed(function () {
// uh oh, where's firstName?
return self.firstName() + ' ' + self.lastName();
});
self.firstName = ko.observable(args.firstName);
self.lastName = ko.observable(args.lastName);
}
Using ViewModel1
will work with no problems. At the point fullName
is defined, firstName
and lastName
is defined so it works as expected. Using ViewModel2
will not work. There would be an Error in the computed function stating firstName
is not defined.
What I've been doing until now was to ensure that all computed observables are defined after all dependent variables have been defined. The problem with this is that in doing so, things are defined in seemingly random places when I would rather keep related variables defined close together.
One nice solution that I've come up with is to define an "initializing" observable set to true
and make all computed observables test if it's still initializing and calculate and return the value when it isn't. That way, the attempts to access the currently undefined variable will not be made.
function ViewModel3(args) {
var self = this;
var initializing = ko.observable(true);
self.fullName = ko.computed(function () {
if (!initializing()) {
return self.firstName() + ' ' + self.lastName();
}
});
self.firstName = ko.observable(args.firstName);
self.lastName = ko.observable(args.lastName);
initializing(false);
}
But this won't be very practical in my case however. I have many computed observables, so doing this in all of them will make it very bloated, remember I have a lot of these. Throttling it doesn't seem to make a difference.
Is there a way to tell knockout to wait before trying to calculate the values of computed observables? Or is there a better way for me to structure my code to deal with this?
I could probably make some helper functions to manage the initialization logic, but I'd still have to alter all of the computed observable definitions. I suppose I can monkey patch knockout to add this initializing logic as I'm not aware knockout has such options which I might just do. I've looked at the source for computed observables before but I don't know if there's already a setting elsewhere.
jsfiddle demo
See Question&Answers more detail:
os