Is it wise to put your ajax calls in your Knockout ViewModel or should it instead be placed in a Model? I’ve come up with a few approaches but none feel completely right.
Approach 1 – ViewModel Only
window.someDataVM = function() {
var self = this;
//used to enable loading indicator
self.pendingLoad = ko.observable(true);
self.myData = ko.observableArray();
self.load = function() {
//make ajax call and populate myData observable array
}
}
Advantages
- Simplest code structure – easier to maintain
Disadvantages
- No reuse for data retrieval
Approach 2 – Model and ViewModel With Callback
window.someDataVM = function() {
var self = this;
//used to enable loading indicator
self.pendingLoad = ko.observable(true);
self.myData = ko.observableArray();
self.load = function() {
someDataM.load(function(data) {
//populate myData observable array
});
}
}
window.someDataM = function() {
return {
load: function(callback) {
//get data via ajax and return via callback
}
}
}
Advantages
-
More code reuse on data retrieval (i.e. one place to load someData)
-
Simpler interface that approach 3
Disadvantages
- Uses callbacks
Approach 3 – Model and ViewModel With Knockout Model
window.someDataVM = function() {
var self = this;
//used to enable loading indicator
self.pendingLoad = ko.observable(true);
self.myData = ko.observableArray();
self.load = function() {
someDataM.load();
}
someDataM.isLoaded.subscribe(function(isLoaded) {
if (isLoaded) {
//populate observable array
}
});
}
window.someDataM = function() {
return {
isLoaded: ko.observable(false);
items: [],
load: function() {
//get some data, populate items, set isLoaded
}
}
}();
Advantages
- Doesn’t use callback
- Keeps data code centralized
Disadvantages
- Will be complicated to have lots of data entry points (i.e. LoadById, LoadByName, etc, etc.)
I personally don’t feel comfortable with self-loading VMs. Thus, I would recommend to load data (model) first, and then pass it to the VM.
Conceptually, it would be something like this:
This kind of approach makes even more sense when VMs are created by other VMs (multi-screen applications). Also, this approach emphasizes on model-view-viewModel separation by making a chain of logical dependency:
However, VMs can re-load data or make asynchronous calls on user interactions. e.g. user may click a button on the page which loads the current time again. These kinds of interactions will happen inside the existing vm obviously. But the question was related to initial load, which I approach this way.