Saturday, August 11, 2012

A Simple Intro to MVC, PubSub and Chaining in 20 Lines of JavaScript

In this post I want to present three JavaScript topics - MVC, Chaining, and PubSub - using the simplest possible examples in code.

Javascript MVC frameworks have been getting a lot of attention, as sites like TodoMVC demonstrate, but the number of choices can be a little overwhelming. It really helps to have a good understanding of MVC first before you delve into some of the more sophisticated frameworks.

MVC


Model-View-Controller is mainly about separating the concerns of managing data (the Model), displaying it to the user (the View), handling user input and performing actions on the data (the Controller).  Exactly how the data in the Model makes its way to the View varies, but organizing your application in terms of these roles can help prevent messy, unmaintainable spaghetti code.

To follow this tutorial, use the Firebug console, or Chrome's developer tools to type in and run the code. Here's the full example running in Firebug; as you can see it's not a lot of code.



As you'd expect, our code will include a model, a view, and a controller.



Our model should hold some data. Since this is the simplest possible example, it will just hold one variable, a counter.



Our View is also going to be super simple: just printing to the console. It has a function called "update" to print something to the console.



The Model and the View don't know about each other directly.  The Controller knows about them both, though, and it handles user input. To keep things simple, user input is via the console, too. The controller is going to have a function called "count" that we'll invoke from the console.



Here it is all together: MVC in 20 lines of JavaScript. After all it's not that complicated.




Each time we invoke the Controller.count function, it updates the model and passes it to the view, which displays the new data.

Of course this is over simplified. It leaves lots of room for improvement. So, let's move on to JavaScript chaining.

Chaining


If we call Controller.count() several times, we can see the data incrementing. If you're using Firebug, you'll see the data being displayed in our View (the console), followed by the word "undefined". That's because the count() function isn't returning any value; the result of running this function is undefined. If count() returned a value, the console would display it instead of "undefined".  So, what if our count() function returned "this", i.e. it returns the Controller object itself? Then when we call Controller.count(), it would return the same Controller object back to us again, and we could do this:


This is pretty easy to implement:



So that is chaining explained with two lines of JavaScript. Neat, huh?  Now let's move on to the last concept.

PubSub


Notice that in the simple MVC example, the Controller updates the Model and then it has to tell the View to update, passing in the new data. This is fine if you only have one view, but what if you have several views of the same model? And what if you want to be able to add or remove views? This is when manually updating views in the Controller starts to become a pain. It would be nice if the model could just announce that it got updated, and the views could listen for this announcement and update automatically whenever the model changed.

Publisher-Subscriber (aka. PubSub) is a variation of the Observer pattern that implements this idea. I'm not going to implement the whole PubSub pattern here, just the bare minimum to get the idea across.

To start with we'll have a list of subscribers, and a function to publish information to them. It just loops through all the subscribers and updates them.


Then our Model needs to announce that it was updated, so instead of modifying the counter directly, we'll give it a function called "incr" and use that to increment the counter. This function will modify the data and then announce it to the subscribers using the "publish" function.


Finally the Controller, instead of manually updating the model and view, will just call the model's incr function and let the PubSub pattern take care of updating the views.


Here it is all together:


So there is MVC, Chaining and PubSub stripped down to their bare-bones essentials in about 20 lines of JavaScript. Obviously, this isn't a complete implementation of any of these patterns, but I think it makes the basic underlying ideas clear.  After reading this and trying out the code in a JavaScript console, you might have a better idea of what a JavaScript MVC framework is good for, how it works, and what kind of features you like best.

6 comments:

~dumski said...

Many thanks! You're the man who showed me all beautifulness of JavaScript!

Daniel Lauzon said...

Great stuff. Essential I'd say!

Anonymous said...

Thanks for this tutorial. It's really cut through a lot of confusion for me. I have 2 questions though. HOw could this be adapted so that the pub.sub function could work for multiple controllers. Secondly, how could I break this code up so that I could put it in separate files and directories?

Darren said...

Anonymous,
Right on. The basic idea for the pubsub is that any time any controller updates any data, any view that is interested in that data will automatically re-render. What I'd suggest is to take a look at a tiny MVC framework called Stapes.JS (http://hay.github.com/stapes/). You'll see that like a lot of MVC frameworks, the way you use it is by creating and extending models (in Stapes, they're called Stapes objects). The reason why you would go to the trouble of creating these special objects to hold your data is because they contain some extra intelligence to emit events when you modify them. That's where the magic comes into play to trigger updates to your views. As for breaking up the code, well... you'll find most JavaScript MVC are all bundled up as small as possible in a single file so they can be quickly and easily downloaded to the browser. That said, if you do need to include multiple dependencies there are a variety of utilities like require.js to do so. I'd also suggest checking out microjs.com (look for client side MVC and also JavaScript loaders).
cheers,
Darren

Anonymous said...

Oh Man. i can't say thank you enough for all you post. Very clear and simple to understand.

Anonymous said...

Best explanation ever and really helpful example! Thank you very much from a JS newbie.

Productivity and Note-taking

I told a friend of mine that I wasn't really happy with the amount of time that gets taken up by Slack and "communication and sched...