There isn’t enough discussion to read about using the model-view-controller architecture for client-side JavaScript web application development and it’s a shame.

History

When I first ventured into client-side web development, “graceful degradation” or “progressive enhancements” were all the rage. Most web pages were required to work in browsers that did not have JavaScript enabled or had very poor JavaScript support. This is still true in many cases. If feature tests showed that the browser supported the required JavaScript then little bits of interactivity were added to the page usually to save full page reloads or make a little animation. The bits of JavaScript were often so small that they didn’t require being architected. Spaghetti code was just fine and anything fancier was overkill.

Early server-side programs were similar. The mainly disorganized CGI scripts accomplished their simple tasks with spaghetti code but they were manageable. Web applications became more sophisticated and the server-side shouldered the brunt of the workload as the client-side was too wimpy to do its share. The server-side code quickly grew larger and required organization. Eventually the Struts server-side web framework came along with its “Model 2” architecture and ever since most server-side web frameworks (e.g. Rails, Catalyst, etc) have touted “MVC” as the optimal way to take requests and generate responses. The request arrives to the controller. The controller gathers data from a model that talks with a database. The controller finally forwards that data to a view template to generate the response. It doesn’t have to work exactly like that but it usually does. Somewhere along the line, the terms “Model 2” and “MVC” became unnecessarily conflated but that can be left as a side discussion. Whatever you call it, the Struts-style architecture has been highly successful at organizing server-side code for its specific request-response functionality and at the very least is MVC-like...but it is definitely not the classic MVC architecture.

It seems that history will mark the release of GMail as the turning point to big “Ajax” client-side web applications. The browsers had grown powerful enough and JavaScript programmers had figured out clever ways of using its features that had been around for a while. The client-side could really start doing some thinking of its own. Full blown “one-page apps” that had functionality resembling desktop application were, and still are, exciting to use. The problem is the code is big and the spaghetti style that is fine for small enhancements is definitely not fine anymore. Some kind of architecture is required for developer sanity.

Many web developers have heard about MVC and have probably been introduced to it in the non-traditional Struts-like incarnation. There have even been attempts to subdue client-side program complexity by porting the Struts-style MVC that was designed for a request-response application to the client-side. Ugg. Not the right choice.

I struggled looking for books on the right way, or even just a good way, to organize large bodies of JavaScript code. I was disappointed that I couldn’t find any. I started venturing out to books on design patterns, Scheme, Java, and one particularly good book ostensibly on ActionScript but more importantly about design patterns. I learned that the classic MVC that started in the Smalltalk world and works so well for desktop applications is a perfect fit for what we do as JavaScript web application developers. I’ve been building JavaScript web applications this way for several years now and feel like there is an MVC hole in my collection articles. This article is an attempt to fill that hole.

Real MVC

In a nutshell the classic MVC architecture work like this. There is a model that is at the heart of the whole thing. If the model changes, it notifies its observers that a change occurred. The view is the stuff you can see and the view observes the model. When the view is notified that the model has changed, the view changes its appearance. The user can interact with the view (e.g. clicking stuff) but the view doesn’t know what to do. So the view tells the controller what the user did and assumes the controller knows what to do. The controller appropriately changes the model. And around and around it goes.

This description is probably a nice summary for those who already understand the MVC architecture but it is way too short for someone who doesn’t know it already. Describing all the ins and outs of MVC is a task I’m not going to attempt in full. I will do three things:

Recommend the books I found that explain MVC well. Describe some tips about MVC for JavaScript web applications in particular that I have found helpful. Provide a complete example.

Good MVC Books

There at two books in particular that were a big help for me understanding the classic MVC architecture.

Without question, the best description of MVC I’ve read is in Colin Moock’s book “Essential ActionScript 2.0”. His follow-up book “Essential ActionScript 3.0” does not contain the in depth MVC discussion so make sure you find the correct edition. Although the book examples are in ActionScript, ActionScript is pretty darn close to JavaScript as they are both ECMAScript implementations. I don’t think I could improve on Moock’s discussions of the observer pattern and MVC. Although I’ve had the book for many years, I re-read the MVC chapter last night and it was a treat. I must be getting old if I’m drawing inspiration from books that are out of print. Maybe actually reading paper books is enough of an indicator that I’m getting old.

The other book I found that covers MVC accessibly is “Head First Design Patterns”. The examples are in Java. Even if Java is not your favourite language, it is a perfectly suitable language for depicting an object-oriented architecture like MVC. This book gives a solid description of why the architecture is beneficial. There is also a section on the Model 2 architecture so you can compare and contrast the two.

Another book worth mention is the standard on design patterns. “Design Patterns: Elements of Reusable Object-Oriented Software” describes the three patterns that comprise MVC: observer, composite, and strategy.

I feel a bit like I’m hawking books here but my earned Amazon affiliate commissions over the years is a measly $9.22. I guess I’m not a good salesman. Anyway these books are all well worthwhile and if you can borrow them from a friend or library then you are doing yourself a great favour.

MVC Example

I was planning on giving the example third but perhaps now is a better time.

The MVC clock example is a port to JavaScript of the MVC example in Moock’s book. It is a fancy stopwatch. There is a single clock model. There are two views, digital and analog, that show the model’s time. There is another view that allows the user to click on knobs and a single controller that knows how to handle those clicks to manipulate the model. Click on “start”, “stop”, and “reset”.

To ensure the MVC concepts are shown clearly I have not used a library like YUI, jQuery, etc. The example uses addEventListener directly. It uses canvas with no feature testing. These choices make the MVC aspects of the example much more accessible which is the goal. Use a modern version of Firefox, Safari, or Chrome and all will be well.

You can meditate on the example for days and days. I have. There is no doubt that you will see bits of the code and decide you would do things differently. If that is the case then both you and the example are doing your jobs well. I use the Scheme-style OOP that Crockford has promoted. You may not. I use has-a composition for the observer pattern. You may use is-a inheritance. I use a pull model for the observer pattern here. You may want to use a push model.

To be completely honest, and slightly heretical, I don’t always use MVC properly myself. Gasp! I haven’t found that the flexibility provided by the strategy pattern (i.e. separation of view and controller) is always beneficial in the type of applications I program. Sometimes I glue them together into widgets and this does save a bit of code. I guess I could tell people I use a model-widget architecture. Here is a MW clock example which is close to how I sometimes program. Please feel free to flame me for my lack of separate views and controllers in the comments but at least do it nicely and in good humour.

MVC in JavaScript

Model

The model is all about data and the model is where the primary data in the system lives.

If your application needs to gather data from the server, local storage, cookies, etc then the model is where this should happen. In fact, the model is the only place in the whole system that should know anything about XMLHttpRequest , for example. You may have code in the model layer that loads code for you and creates model objects. For example,

// model constructor function // var makeEmailModel = function(data) { // ... return { // ... }; }; // load data from the server and create // model objects // var loadEmails = function(callbacks) { doXHR('GET', '/emails', { on200: function(xhr) { var emailsData = fromJSON(xhr.responseText); var emails = []; for (var i=0, ilen=emailsData.length; i<ilen; i++) { emails.push(makeEmailModel(emailsData[i])); } callbacks.success(emails); }, onComplete: function(xhr) { callbacks.failure(); } }) };

The loadEmails function above is called by bootstrap or controller code. Give the callback properties names like “success”, “failure”, and “invalid” that are semantically meaningful to the calling code as shown in the example. Don’t use names like “on200”, “on500”, and “on400” as those names are related to the mechanics of the actual data getting. Only the model should know about those mechanics and those mechanics may change.

When the model notifies its observers, the model should tell the observers what happened to the model. A model may say “hey, I changed!” and the observers then decide what they do based on that information. The model should never tell the observers what they need to do. The model will never say “you need to redraw the time you are displaying” because the model has no idea if there is any view that is drawing time. The model is oblivious to what its observers might be doing.

The model can notify different groups of observers. For example some observers may only be interested in withdrawals in the following account model.

var makeBankAccountModel = function() { var balance = 0; var depositObservableSubject = makeObservableSubject(); var withdrawalObservableSubject = makeObservableSubject(); var deposit = function(amount) { if ((typeof amount !== 'number) || (amount <= 0)) { throw new Error('deposits must be positive numbers'); } balance += amount; depositObservableSubject.notifyObservers(amount); }; var withdrawal = function(amount) { if ((typeof amount !== 'number) || (amount <= 0)) { throw new Error('withdrawals must be positive numbers'); } if (amount > balance) { throw new Error('not enough money in account'); } balance -= amount; withdrawalObservableSubject.notifyObservers(amount); }; return { getBalance: function() { return balance; }, addDepositObserver: depositObservableSubject.addObserver, addWithdrawalObserver: withdrawalObservableSubject.addObserver }; };

Note also that this bank account example pushes the amount of the deposit or the withdrawal to the observers. That information is not available for the observers to pull from the model so pushing the data helps give a more complete description of what happened to the model.

If you have limited time for code reviews start by reviewing the models.

The model definitely should know nothing about the DOM. That would be a break down of all that is good.

View

The view is what you see and what you click.

The view is the only part of the system that knows about the DOM. (Except perhaps some of the bootstrapping code.)

Do your best to avoid using id attributes on DOM elements in the view. The ability to put two views of the same class in a page twice depends on not having the same id value in the two views. There are other strategies but you should not need to find elements in the page if your views are well constructed.

When a user clicks on something in the view, the view is too stupid to know what to do. In the clock example, when the user clicks the “start” button, the view doesn’t understand the user’s desire to get the clock model to start ticking. The view has to rely on its controller to make that decision. Since the view doesn’t know what should be done, it should be calling methods on the controller that explain what happened in the view. For example, the view could call a controller method named “handleStartClick”. The view should not be calling a controller method named “startTheClock” because ironically some other interchangeable controller may not start the clock when the start button is clicked.

A templating library that generates HTML is not a view and this is a confusion that seems to come from the Struts-style architecture. In the classic MVC, a view is a living object that observes a model and stays in sync when the model changes. A view might call a templating library to help update the view but the template is not the view. For example,

var makePersonView = function(personModel) { var rootEl = document.createElement('div'); var personModelChangeObserver = function() { // The view calls a templating library to generate // HTML that shows the person's name. rootEl.innerHTML = processTemplate('personTemplate', {name: personModel.getName()}); }; personModel.addObserver(personModelChangeObserver); return { // ... }; };

Controller

The controller is a decision maker. When a user clicks in the view, the view forwards that event on to the controller so the controller can decide what needs doing.

There is nothing particularly JavaScript-specific about controllers. They don’t know about host objects like those in the DOM or about XMLHttpRequest.

Controllers mutate models and sometimes mutate views. Controllers mutating views is often not the right choice however. Suppose you have a to-do list web app. When the user checks an item as completed, the click is forwarded to the controller. The controller modifies the model item as complete which makes an async save to the server. The controller could tell the view to show a throbber while the model is saving. For example, suppose we have the following to-do model

var makeTodoModel = function() { // The id is used in server's database. // It is set here when to-do is created. var id = ''; var complete = false; var saving = false; var changeObservableSubject = makeObservableSubject(); // ... var saveAsComplete = function(callbacks) { saving = true; changeObservableSubject.notifyObservers(); doXHR('POST', '/todo/'+id+'?complete=true', { on200: function(xhr) { saving = false; complete = true; changeObservableSubject.notifyObservers(); callbacks.success(); }, onComplete: function() { saving = false; changeObservableSubject.notifyObservers(); callbacks.failure(); } }); }; return { saveAsComplete: saveAsComplete, addChangeObserver: changeObservableSubject.addObserver, getId: getId, getDescription: getDescription, getComplete: getComplete, getSaving: getSaving // ... }; };

There are two ways we could use the above model in the view and controller interaction. The first is the controller turns the throbber on in the view.

var makeTodoView = function(todoModel, todoController) { var rootEl = document.createElement('div'); var todoEl = document.createElement('div'); rootEl.appendChild(todoEl); var throbberEl = document.createELement('div'); throbberEl.style.display = 'none'; throbberEl.innerHTML = 'saving...'; rootEl.appendChild(throbberEl); var todoModelChangeObserver = function() { todoEl.innerHTML = (todoModel.getComplete() ? 'complete' : 'incomplete') + ': ' + todoModel.getDescription; }; todoModel.addChangeObserver(todoModelChangeObserver); var showThrobber = function() { todoEl.style.display = 'none'; throbberEl.style.display = ''; }; var hideThrobber = function() { throbberEl.style.display = 'none'; todoEl.style.display = ''; }; todoEl.addEventListener('click', function() { todoController.handleTodoClick(todoModel); }, false); // ... return { showThrobber: showThrobber, hideThrobber: hideThrobber // ... }; }; var makeTodoController = function() { var handleTodoClick = function(todoModel) { if (!todoModel.getComplete()) { todoModel.showThrobber(); todoModel.markAsComplete({ success: function() { todoModel.hideThrobber(); }, failure: function() { todoModel.hideThrobber(); } }); } }; // ... return { handleTodoClick: handleTodoClick // ... }; };

The potential problem with the above example is if the to-do is represented in the UI twice and that when the to-do is being saved to the server both representations should show the throbber. The fix is to have the view show the throbber based on the model state. Since the model above notifies observers when it is saving we can write the following for the view and controller.

var makeTodoView = function(todoModel, todoController) { var rootEl = document.createElement('div'); var todoEl = document.createElement('div'); rootEl.appendChild(todoEl); var throbberEl = document.createELement('div'); throbberEl.style.display = 'none'; throbberEl.innerHTML = 'saving...'; rootEl.appendChild(throbberEl); var todoModelChangeObserver = function() { if (todoModel.getSaving()) { showThrobber(); } else { hideThrobber(); } todoEl.innerHTML = (todoModel.getComplete() ? 'complete' : 'incomplete') + ': ' + todoModel.getDescription; }; todoModel.addChangeObserver(todoModelChangeObserver); var showThrobber = function() { todoEl.style.display = 'none'; throbberEl.style.display = ''; }; var hideThrobber = function() { throbberEl.style.display = 'none'; todoEl.style.display = ''; }; todoEl.addEventListener('click', function() { todoController.handleTodoClick(todoModel); }, false); // ... return { // ... }; }; var makeTodoController = function() { var handleTodoClick = function(todoModel) { if (!todoModel.getComplete()) { todoModel.markAsComplete({ success: function() { // nothing to do }, failure: function() { // nothing to do } }); } }; // ... return { handleTodoClick: handleTodoClick // ... }; };

Because the view now depends on the model’s saving state data, all instances of the same to-do visible to the user will stay in sync: when the model is being saved all related views will show the throbber. This is the power of the primary data kept in the model.

Boostrap

The boostrap code is likely a bit messy to get the whole big one-page app up and running at window.onload . Models, views, and controllers need to be created and their relationships need to be established. There is no standard way to do these things. Find a way that is appropriate for your application and try to keep the mess as small as possible.

Conclusion

Hopefully everyone noticed that an MVC framework is not required to build an application using the MVC architecture. It is great that people get excited about a new design pattern or architecture but that doesn’t mean it needs to become a framework. A few helper libraries like the library for observable subjects is sufficient.

Using an MVC style has helped me tame the complexity of some of the bigger projects I’ve work on and it might help you too. If you have experience with the MVC architecture then please leave a comment with any tips that you’ve discovered about how to implement using MVC in the JavaScript world. I’m very curious what others have learned through their experimentation.

I've added another example in a follow-up article: MVC To-Do Application