Big fat View Model

KnockoutJS is a beautiful JavaScript framework that helps you to create responsive and data rich user interface without making the JavaScript code dirty. When you start learning KnockoutJS, you tend to make habit of creating single a View Model and binding it globally or to one particular DOM and play with it.

Once you start diving deep, you will realize, single view model is not enough for developing enterprise applications. It does not help you maintaining the modularity of an application. Bigger the application gets, complexity of the view model increases. You will literally give up as the code will become hard to maintain and even harder to test.

Goodness of multiple view models

Why one should use multiple view models? Answer is simple, it maintains the modularity. Multiple view models act as modules. You basically divide big problem into small modules which are reusable and easily extensible. You preserve ability to maintain an application by decoupling the components to the most granular level. The app flow becomes so good with introduction of multiple view models in your app.

However, there is a major and most discussed problem with this approach on KnockoutJS section of StackOverflow, Knockout forums (common question with MVVM pattern in general). The communication between multiple view models. Surprisingly, this problem has not been addressed or discussed at all in KO documentation even though people face it quite often. I end up helping at least couple of KnockoutJS users / developers on Stack Overflow having the same problem.

Yes, we don’t have out of the box and easy to use solution on communication between multiple view models which are bound in a single viewport but with different DOM elements like the code below.

 var viewModel1 = function(){
	var self = this;
	self.firstName = ko.observable();
	self.lastName = ko.observable();
	self.fullName = ko.computed(function(){
		return self.firstName + " " + self.lastName;
	});
};

var viewModel2 = function(){
	var self = this;
	self.premium = ko.observable();
};

ko.applyBindings(new viewModel1(), document.getElementById("container1"));
ko.applyBindings(new viewModel2(), document.getElementById("container2"));

There are two correct approaches of maintaining communication between multiple view models.

  1. Maintaining master view model
  2. Introducing pub-sub

Maintaining master View Model

First way of achieving communication between multiple view models is to introduce master view model.

// view model 1 definition
var viewModel1 = function(){
 this.firstName = ko.observable("Wrapcode");
 this.messageForVM2 = ko.observable("Hello from first view model");
 this.message = ko.observable("Hello this is vm1")
};

//view model 2 definition
var viewModel2 = function(vm1){
 this.firstName = ko.observable(vm1.firstName());
 this.message = ko.observable(vm1.messageForVM2());
};

//master view model with instances of both the view models.
var masterVM = (function(){
 this.viewModel1 = new viewModel1(),
 this.firstName = "Rahul",
 this.viewModel2 = new viewModel2(this.viewModel1);
})();

ko.applyBindings(masterVM)

Live action on JSFiddle : http://jsfiddle.net/rahulrulez/paxnd6uz/1/

With this approach, it’s easy to get / post information from / to instance of other sub view models in master view model. But the problem of publishing changes still persist. Although we found a way to exchange data between multiple view models, the communication is still passive, if we change something in input box in the fiddle above, it does not get reflected in to another view model. That’s where pub/sub pattern, our next approach plays an important role.

Pub-sub in Knockout, with Postbox

Well, if you go with master view model approach, you will still have to refer / pass one view model into another to communicate. The communication is still pretty much passive as master view model approach does not observe or track the changes. To overcome this limitation, KnockoutJS has its native PubSub function – ko.subscribable which is not at all documented in detail. Here’s how you can extend your application to integrate PubSub.

Construct an object of ko.subscribable

var shouter = new ko.subscribable();

Subscribable is inherited so it is available globally irrespective of any view model scope. But it’s better to construct it globally so that you don’t have to create multiple instances of same thing.

Notify changes via topic

Now, notify the changes to shouter (subscribable) by using Knockout’s subscriber function. In the above example (refer JSFiddle), we want to publish the value this.messageForVM2 from viewModel1 so that it will be accessible in second view model. We can achieve this by notifying subscribers in subscription function of this.messageForVM2 Learn more about Subscribe.

this.messageForVM2.subscribe(function(newValue) {
 shouter.notifySubscribers(newValue, "messageToPublish");
 });

Remember, ko.subscribable.notifySubscribers takes two parameters.

  1. Value you want to notify to the subscribers –  Value can be anything. Number, string, object, function… Literally anything.
  2. Topic name – Topic name is just unique string which will distinguish multiple subscriptions.

Subscribe to the topic

To be able to catch published values and updates, you need to subscribe to the particular topic. Whenever the change gets triggered, it invokes the subscriber function. We can write the needed logic inside subscriber function. Subscribable exports method subscribe which expects three parameters

  1. callback – the function you are expecting to invoke when value of the subscribed topic is changed.
  2. callbackTarget – if you provide callbackTarget, callback is bound with the target such that content of target scope is accessible in callback function.
  3. event / topic – topic name / identifier.
shouter.subscribe(function(newValue) {
     this.message(newValue);
 }, this, "messageToPublish");

Working fiddle

Try typing something in the message box, subscribable will publish the message as soon as observable tracks changes. Now the communication is active and decoupled. We do not have any sort of reference in both  view models.

You can do a lot with KO’s native PubSub. You should definitely try Ryan Niemeyer’s very useful extension called Postbox. It extends simple pub/sub capabilities to the next level by introducing various customizations like syncing multiple observables / components from multiple view models, publishing messages on initialization, clean subscription and unsubscribing abilities etc.

Knockout-Postbox in action :

I hope this article will help to give you a little head start if you are planing to decouple modules without losing communication in your Knockout application.

Peace,
RP 

 

  • Paul

    Thank you very much, mate! It helped me a lot!

  • Excellent stuff Rahul!

  • César Nobre

    Awesome post, Rahul!

  • antiplaka

    thanks dude

  • Jan V

    The sample fiddles no longer work, JSFiddle now links/serves every fiddle via HTTPS and if you reference a script via HTTP some/all browsers refuse to load it (eg. the knockout js)

    Fixed “Subscribe to topic” fiddle: https://jsfiddle.net/janv8000/gn16ho8s/1/

    • Thanks for the update Jan 🙂 Will edit it.

  • Pingback: Communication between multiple View Models in Knockout - My Daily Achievements()

    • Tamil

      Thanks for the good article Rahul. Why should we follow this approaches? ko.utils.extend can do the communication easily between the multiple view models. right.. correct me If am wrong.

  • Archit

    Nice article something which I was desperately looking for. Great work.

    Doubt :
    What to do if my 2 viewmodels are in separate javascript files.

    Scenario :
    I have a file graph.js and other file which header.js. User selects the input from time selection menu which is in header.js file and I
    want to update graph.js based on header.js’ selected parameter. So how can I do that?

    • Jasper

      If you’re still looking for a solution (or if others want to know):
      Knockout registers as a global ‘static’ object, so you can use the plugin Postbox mentioned in the post as well as every technique that extends Knockout from both files – within the same page of course.
      (For multiple viewmodels in different tabs/browsers/on different machines though, you will have to use network-aware solutions like SignalR, MQTT or alike)

  • Pingback: When to create new view models in Knockout.js - CSS PHP()

  • @$#@()$* perfect

  • Anyone knows how to send functions between 2 different vm? is it possible?