JavaScriptMVC Anti-Patterns

Posted by & filed under Front-End Development, JavaScript.

In early 2011, I was faced with the task of recommending a framework for a complex JavaScript application. I resisted the urge to build my own MV* framework and instead opted to learn how to use one of the battle-tested options. I considered several frameworks including JavaScriptMVC, Backbone and KnockoutJS before ultimately deciding on JavaScriptMVC. (If I were doing this today, I’d probably include Ember, AngularJS and a few others in my assessment.)

JavaScriptMVC is a great MV* framework that comes pre-rolled with a lot of the solutions developers need in order to build complex JavaScript applications. Among the problems JavaScriptMVC solves are dependency management, templates, unit tests, build scripts, and documentation. The framework allows developers to focus on building an application instead of assembling a frontend stack or writing yet another MVC-esque JavaScript framework.

While working with JavaScriptMVC for the better part of 18 months, I identified some anti-patterns that can wreak havoc on application development. In order to take full advantage of JavaScriptMVC, you should avoid these patterns.

Using JavaScriptMVC when you don’t really need it
Before discussing anti-patterns in a JavaScriptMVC application, let’s consider whether an application needs an MVC framework at all. Are you building a single-page web application in which the frontend only communicates to the server to retrieve data? If so, you should consider a frontend framework. However, if your application is built around a page-based architecture in which the backend is serving views in addition to data, then it is unlikely that you will benefit from a frontend MV* framework. You should also steer clear of frontend frameworks if you aren’t familiar with an MV* design pattern (such as MVC, MVVM or MVP) and you are unsure of how to separate the concerns of an application into models, views and controllers. Now, on to the actual anti-patterns.

Manually binding event handlers
JavaScriptMVC has a built-in way of binding event handlers that should always be used instead of manual binding. For example in JavaScriptMVC, instead of doing this:

$.Controller('MyExample', {}, {
    init : function() { $("#myElement").click(function(){...})}
});

You should bind event handlers using the method prescribed by the framework:

$.Controller('MyExample', {}, {
    '#myElement click' : function() {...}
});

There are numerous advantages to binding handlers the JavaScriptMVC way. One advantage is that the Controller will automatically delegate event handling to the controller’s element. This results in a smaller memory footprint and better performance. Controllers will also automatically unbind correctly-bound handlers when the destroy method is called. This will free memory when the Controller’s element is removed from the DOM and the Garbage Collector runs. Lastly, bound handlers are automatically scoped to the Controller’s DOM element which serves to encapsulate Controller logic.

Declaring markup in Controllers
Generating markup in Controllers is another anti-pattern that undermines efficiencies gained by using JavaScriptMVC. If you’ve used JavaScript (or jQuery) to add basic interactivity to a website you are probably guilty of using the following pattern:

$("#myElement").html(["<p>", myVar, "</p>"].join(""));

This may seem innocent enough, but imagine using this anti-pattern when more complex markup is required (like a datagrid). You would quickly create a nightmarish array of tags that would be impossible to maintain. If I inherit code like this from you, I will hunt you down and make you pay! But more importantly, if you inject markup into the DOM this way you’re not taking advantage “V” part of JavaScriptMVC. Here’s the JavaScriptMVC way of getting markup into the DOM:

/* Controller */
$("#myElement").html($.View("myView.ejs", {myVar: myVar});

/* Template */
<p><%= myVar %></p>

The above example is a bit simplistic but it’s important to understand how JavaScriptMVC Views improve your application. The View class abstracts the frontend templates from the Controller allowing an application to make use of one or more JavaScript templating languages (EmbeddedJS, JAML, Micro and jQuery.Tmpl by default.) Extending the View class will allow you to use additional template languages as well. Also, by stealing views (i.e. including them via JavaScriptMVC’s dependency loader) you are adding them to the list of assets subject to concatenation and caching by the JavaScriptMVC build script. This will reduce HTTP requests and ultimately produce a faster application. Using frontend templates helps you write readable, easy to maintain and reusable markup.

Replacing node contents instead of using $.View
Another View-related anti-pattern involves replacing individual node contents from the Controller instead of using the View like this:

$.Controller('DataGrid', {}, {
  "{model} data" : function(DataGrid, ev, data) {
    for(var i = 0, ilen = datagrid.rows.length; i < ilen; i++){
      $("tr").eq(i).html(["<td>", data.rows[i].column1, "</td>"].join(""));
    }
  }
});

In the above example, we are accessing the DOM multiple times in an effort to replace part of the markup of the DataGrid. A better pattern is to let the View work automagically update the DOM like this:

$.Controller('DataGrid', {}, {
  "{model} data" : function(DataGrid, ev, data) {
    $("table").html($.View("table.ejs", {rows: data.rows});
  }
});

This reduces the number of times the DOM is accessed and once again keeps the Controller free of markup.

Failure to call _super() when overriding destroy
The life of a page in a single-page web application is much longer than the life of a page in a traditional web application. This means that small memory leaks can add-up over time resulting in a sluggish, possibly unusable application. Luckily JavaScriptMVC provides a clear pattern for avoiding such memory leaks through the Controller’s destroy method. This is one of my favorite features of the framework – memory leak prevention via automatically unbound and undelegated event handlers.

The $.Controller.destroy() method is automatically called when the related element is removed from the DOM. But it’s common for objects that extend $.Controller to perform teardown operations before destroy runs. In this case, a controller would override the destroy method like this:

$.Controller('DataGrid', {}, {
  destroy : function() {
    //perform controller-specific teardown tasks
    this.element.html("You destroyed me");
    //if you forget the next function call, bad things will happen
    this._super();
    }});

Calling this._super() triggers the $.Controller to perform it’s default teardown operations such as unbinding and undelegating event handlers. If you don’t call this._super() when overriding the destroy method, your controller’s handlers remain bound and delegated which keeps a reference to the controller in memory. Objects remain in memory as long as they are referenced, so call this._super() when overriding destroy to prevent memory leaks in JavaScriptMVC applications.

Using DOM node attributes instead of model attributes.
I often use HTML5 custom data attributes to store information that isn’t of immediate importance to the user but may be relevant in the future. Like this

<ul>
    <li id="brian" data-kingdom="animalia" data-phylum="chordata" data-clazz="mammalia" data-order="primata">BRIAN</li>
</ul>

This is ugly, but it’s convenient if you want to alert the user of the biological classification of an organism on click like this:

var brian = document.getElementById("brian"),
            clickHandler = function(event){
                var el = event.target;
                alert( [el.innerHTML, "is a", el.dataset.kingdom, el.dataset.phylum, el.dataset.clazz, el.dataset.order].join(" ") );
            };
brian.addEventListener("click", clickHandler, true);

However, in JavaScriptMVC creating a model to store the organism details would be much cleaner. To refactor the above code, we would first create an organism View:

<li <%= this %> ><%= this.name %></li>

Then an organism Controller:

$.Controller('Organism', {listensTo: ['click']}, {
  init : function() {
    var model = $.Model.extend({},{name: "Brian", kingdom: "animalia", phylum: "chordata", clazz: "mammalia", order: "primata"});
    this.element.html($.View('organism.ejs', model);
    },
  click : function(el, ev) { alert( [el.model().name, "is a", el.model().kingdom, el.model().phylum, el.model().clazz, el.model().order].join(" ") };
});

This pattern keeps the markup clean as the complexity of the organism object increases.

There are a few additional anti-patterns that don’t warrant a lengthy explanation like the ones above:
Manipulating data in the View
If you need to manipulate the data being rendered in your View, do that in your Model, not the template.

Accessing DOM from a Model
Your Models should have no knowledge of the DOM, so if your model queries the DOM you’ve done something wrong. If your model needs external data, pass it to the Model via the attr method.

Using OpenAjax(global pub/sub) instead of events. (<3.1)
If your application uses multiple instances of the same controller at once, global pub/sub is a terrible way to notify your views of changes in the model. Use model attribute binding instead.

Failure to use JSLint as you go
The JavaScriptMVC build script opens your JavaScript files in Rhino, which has little tolerance for seemingly-innocuous problems in your files (like trailing commas in object literals.) You should use JSLint as you go or run steal.clean before your build. (Note: If you use TextMate, check out the JavaScript Tools bundle which notifies you warnings/errors as you save)

These are just a few of the anti-patterns that can plague single-page JavaScript application development. By avoiding these anti-patterns and using best practices, you can appreciate the power of JavaScriptMVC.

6 Responses to “JavaScriptMVC Anti-Patterns”

  1. patrick clancy

    Hello Brian
    I very much enjoyed your outline of JMVC anti-patterns. Our team has been using JMVC for a few months now and have worked to avoid most of these. Thanks for taking the time to publish this article.

    Reply
    • Brian Hadaway

      @Derek – JavaScriptMVC will automatically append generated class names to the classes you specify. In your example you would do this:
      <li <%= this %> class="my_classname" > <%= this.name %></li>
      And the following would render:
      <li class="my_classname generated_classname" >Model Name</li>

      Reply

Leave a Reply

  • (will not be published)

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>