Let's say I have the following durandal code:
define(['durandal/app', 'plugins/http'], function (app) {
var vm = function(){
var self = this;
self.myData = ko.observableArray([]);
}
};
vm.activate = function() {
this.myData([]);
};
vm.deactivate = function(){
};
return vm;
};
I know by returning a constructor function, every time the view model is activated, it will return a new instance.
My question is: is there anyway that when I access, if there's myData()
from previous access, then I don't want to set this.myData([]);
but to use the previous myData()
?
I know by returning a singleton object can do it, but if I want to keep the constructor function, can I do it?
Another question, what's the point of having activate and deactivate in the above code, if you are going to get new instance, therefore guaranteed a 'clean' object anyway?
Yes, you can do that.
For example, in our application, each module has two parts: a singleton module following the naming convention of XxxServices, and an instance module, called Xxx, where Xxx is the canonical name of your module:
There are [at least] two ways to make services available to instances: through Durandal composition, passing in an
activationData
object, and through dependency injection using RequireJS.(Another way is to use a client-side message bus, such as postal.js, under a request-response scenario. But that's a horse of a different color).
If you're not familiar with these techniques, I can elaborate if you wish.
There are several approaches you can take, which I enumerate below.
For all of the examples below, consider that Projects is the topic of choice.
INJECTION (NO VIEW)
With this approach, we inject a ProjectsServices module (singleton) into a Projects module (instance). But this approach works only if ProjectsServices does not also offer up one or more views. Below, I'll show you what we can do if our services module also, itself, offers one or more views.
ProjectsServices ViewModel (singleton) sevicesProjects.js
Projects ViewModel (instance) projects.js
HOST-CLIENT (VIEW)
With this approach, the Projects module is composed inside of the ProjectsServices module, and we pass
myData
back and forth through an observable onactivationData
. Also, this approach assumes that the services module is not only offering code services, but also view services. A global "Add Contact" form that pops over other forms is an example of a view-based services module. And, of course, the "Add Contact" view would have a viewModel behind it, which represents the code services for adding a contact.ProjectsServices ViewModel (singleton) servicesProjects.js
ProjectsServices View servicesProjects.html
Projects ViewModel (instance) projects.js
Projects View projects.html
PUBLISH-SUBSCRIBE
With this approach, we leverage Durandal's built-in pub/sub facilities through
app
. This approach can be used either with Injection or Host-Client given above. The strategy is to publish a request message from theactivate
handler of the instance module, and receive a reply message within the same handler, the purpose of both messages to request and supplymyData
(which was saved off earlier, presumably). When we're ready to savemyData
back to the services module, we send another message withmyData
as the payload.ProjectsServices ViewModel (singleton) servicesProjects.js
Projects ViewModel (instance) projects.js
The views don't change in this case, so they're not provided here.
MESSAGE BUS
This approach is virtually identical to the pub-sub approach, except that we're using a client-side message bus, such as postal.js. It's more refined, as you will see. It also happens to be the approach that we take in production. This approach should be used with the Host-Client approach above, only we're simply passing in a message channel, not the data itself.
ProjectsServices ViewModel (singleton) servicesProjects.js
ProjectsServices View servicesProjects.html
Projects ViewModel (instance) projects.js
Note that the Projects view doesn't change in this case, so it is not included here.