Part 2: Backbone.js + Require.js, Further Modularization and Just in Time Dependency Loading

Blog
The best minds from Teradata, our partners, and customers blog about whatever takes their fancy.
Teradata Employee

This article is a continuation of Part 1: Backbone.js + Require.js. It outlines further modularizing the Todos application presented in part 1 and introduces “just in time” module loading. 

Introduction

If you are not familiar with Backbone.js and Require.js then I suggest reading some articles at Backbone Tutorials and the first part of this series. If you still wish to read on after that then something is terribly wrong with you.

Step 1: Organize

The first thing I did was split the models, views, and collections into separate files: https://github.com/jstrimpel/backbone/tree/master/examples/todos-requirejs-2. I also, moved the templates in index.html to their own files. The templates are located in directories that correspond to their view names. This structure should look pretty familiar if you have worked with any MVC solutions in the back-end or at least make sense if you understand MVC basics. If you have not clicked on the link above I highly advise doing so because it provides a nice visual of the application structure.

At this point you may be asking, but wait where are the controllers from the C in MVC? There aren’t any in this example. Backbone has controllers, but they are called routers. We will not be using routers in this series because the application is simple, so too bad.

Step 2: Update Application File

I renamed the application file from “todos.js” to “app.js” because this made more sense after organizing the code, and it is a more standard naming convention.  App.js looks significantly different from todos.js in part one. Now when app.init() is called in main.js after the bootstrapping process it creates a Todos collection and passes a reference to this collection to the Todo model upon creation. Next it sets the model in the Todos collection to the Todo model just created. The reason for this initialization hackery is that the Todo model relies on the Todos collection count when creating a new Todo model. Additionally, the Todos collection uses the Todo model as its template when adding a model to the Todos collection because it relies on the Todo model defaults. The original Todos application was relying on the global name space to accomplish all this, but we are better than that and like to keep things loosely coupled. Finally, it passes a reference to the Todos collection to the main application view and creates the view. If none of this makes much sense then take a look at the code below. Disclaimer: there is probably a better way to do this, but like you I am learning as I go. If you know of a better solution please fork the code on GitHub, improve it, and add a comment to this article.

define (['collections/todos', 'views/app', 'models/todo'], function (todosColl, appView, todosModel) {
'use strict';

var module = {};

module.init = function () {
var collection = todosColl.create(),
model = todosModel.create({ 'collection' : collection });

collection.model = model;
return appView.create({ 'collection' : collection });
};

return module;
});

Step 3: Update Collection

Nothing much changed in the Todods collection. I removed the reference to the global Todo model because it gets set when app.init() is called.

Step 4: Update Model

Same as the collections – I removed the global references. The Todos collection is passed into the model when it is created in app.js. I am trying to create good interfaces to these modules so that they can be easily reused and refactored.

Step 5: Update Views

The first thing to take note of in both views is that the templates are listed as module dependencies. The templates were removed from index.html, since *.html files typically are not cached and it makes the application more scalable. If we were to put every template for a large application in index.html this would become unmanageable very quickly. Also, we get a performance gain by making index.html smaller and making the template files cacheable. The benefits of this approach are negligible in an application this size. However, these optimizations become increasingly important as an application grows.

Here is the coolest part. In app.js at line 55 I have wrapped the section of code that creates a Todo view in a require statement.

require(['views/Todo'], function (todoView) {                                                           
var view = todoView.create({ model : todo });
that.$('#todo-list').append(view.render().el);
});

What is so cool about this? Nothing really in this example, but in larger applications this is a really nice design pattern. Instead of loading the kitchen sink or loading every dependency for a module or interface you now only have to load what you need when you need it. For instance, if you have a very complex user interface with numerous dependencies, why load all dependencies if some of them may never be executed? Another aspect I really like about the require statement is that it is simple. You do not have to pass around callback functions, worry about browser quirks when loading files, or be overly concerned with scope. All typical dependency concerns have been abstracted away allowing you as a developer to focus on application development rather than the dependency management implementation. 

Conclusion

Hopefully this was informative and easy to follow. I am not always communication great at, so if this is complete rubbish and you are not my spouse (she has already told me repeatedly that I am not a good communicator) then feel free to drop me a line informing me of my communication faults. 

The Saga Continues

If you were planning on repeatedly ramming your head into brick wall for ~10 minutes in the near future then here are some slightly less painful and possibly more entertaining options.