Backbone App - End to End: Basic design pattern

In this article we apply the Model, Collection and View logic from the previous article on Backbone.js basics to an actual useable design pattern.
It's a rather simple one but versatile enough to implement a SPA that connects to the IMDB's external API.

From the previous article, one thing that jumps into mind is that if we keep adding logic to the index.html file, before we get anything remotely decent going on, we will have hundreds of lines of code straight, with no organization whatsoever. Sounds like a legacy nightmare.

We shall fix that.

Create the folder structure

First, remove the previous <script> block we used to include all of our backbone testing code. Then, we are separating all the file types, for better organization. Let's use this structure:

\css
    main.css
\js
    \collections
    \models
    \views
    App.js
    Router.js

So, the main.css file is the one that will contain all the custom CSS we make. Any other CSS files should live inside the \css folder.
Inside the \js folder we are splitting all the files into the according logic. That will be a lot easier to maintain.
There are also the App.js and the Router.js files. I'll get to those in a while.

There is a problem with the current setup, however. Splitting the code into separate files makes them invisible to each other, meaning that if in the movie View you want to call onto the movie Model, that just wont happen. The same for every file, including the Router.

Also, there is nothing actually running all the code once you open the index.html on the browser.

Linking the files together

A simple solution for this problem is to create a global Javascript object that is visible to all the folders and files, and add all the Models, Collections and Views to it.

Copy paste this into your App.js file:

var MovieApp = {

    Views: {},
    Models: {},
    Collections: {},
    Router: {}
}

$(document).ready(function(){
    // Add some code here
});

And add the App.js file to the index, just below the closing </body> tag:

<script type="text/javascript" src="js/App.js"></script>  

Now, the App object is visible to all the files and folders. Notice it has empty properties for Views, Models, Collections and Router. Here is where we are going to anchor all our backbone parts. Making them globally accessible through the App object.

For example, create a Movie.js inside the \models folder:

MovieApp.Models.Movies = Backbone.Model.extend({

    initialize: function(options)   {},
});

then add it on index.html just below the place where you added the App.js reference, like so:

<script type="text/javascript" src="js/Models/Movie.js"></script>  

Hence, creating the object itself, and putting it in the App.
At the moment, doing this:

var m1 = new MovieApp.Models.Movies();  

anywhere will create that Movie Model.
Do the same for the Movies Collection, and the Movie View.

Ok, right now everything is in it's place, all we need is to start this. Nothing is happening at the moment because there is nothing executing anything. Let's hook this up with the Router.

Router

The backbone Router is what's generates the SPA (Single Page Application) behavior, meaning, it's what makes you navigate through pages and content without having the browser refresh the page.

How does it work?

Each page in backbone, being home, contacts, search, anything is actually just a regular View. Just like any other view. The only difference is that we set the Router to apply that specific view to the browser. So every time we navigate to some page, the Router is just replacing the current HTML with the HTML that belongs to the next page's el, or $el, or whatever you want to call it.

Configure it

The creation of the Router is, surprise surprise, simple.
Open your Router.js and add:

MovieApp.Router = Backbone.Router.extend({

    routes: {
        'home': 'home',
        '*path': 'home'
    },

    home: function(){}
});

Add the Router.js to your index.html file, the same way you did with everything else:

<script type="text/javascript" src="js/Router.js"></script>  

Just remember to keep it always as the last file added.
Now, in App.js initialize the Router, like so:

$(document).ready(function(){
    MovieApp.Router.Instance = new MovieApp.Router();   
    Backbone.history.start();
});

Router structure & logic

The Router can be divided into two different things:

  • routes object: Right on top of the file, you can see a routes object. Thats what makes a check between whatever URL is the browser navigating into and the routing function
  • routing function: What logic is preformed according to the browser's URL

For example, if look at the current configuration of our Router, if your App is to navigate into /home.html, the Router will trigger the home function. Simple hum?
The '*path': 'home' part simply defines the home function as the default thing to trigger.

Now let's make the Router actually route to someplace, naming home. Since the pages we navigate into are just regular views, let's create the home view, so we can navigate into.
Create a home.js inside the \views folder:

MovieApp.Views.Home = Backbone.View.extend({

    initialize: function(options) {},

    render: function()
    {
        this.$el.html("This is my home page!!");       
        return this;
    }
});

Edit the Router's home function:

home: function(){  
    var view = new MovieApp.Views.Home();
    $('#main').html(view.render().el);
}

Notice that the home function is getting the home view, executing the render() and replacing our #main div's content with it's own content.

Hopefully when refreshing the page, you should see the incredibly farfetched text "This is my home page!!" there. What happened was that the Router has been initialized on page load, and executed the default function home, awesomely rendering the home.js view's HTML onto the browser. Exciting times.

Just copy the exact same behavior for a Search page, and in index.html add a menu outside your #main div, like so:

<ul>  
    <li><a href="#home">Home</a></li>
    <li><a href="#search">Search</a></li>
</ul>  
<div id="main"></div>  

Once everything is into place, voilá, you can navigate between both pages.

Templating with Mustache

In our current example we are simply writing basic strings onto our views or pages. That's fine for demonstration purposes on how backbone views work, however, a page is usually more complex that just a string. Hopefully you can already see how the current populating of complex views by putting it's content inside the .html() function can be ugly and complicated. To solve this, we will use a template system (actually the javascript version of it) called Mustache.

Next we will show external content on the Search page, connecting it to an external API.

comments powered by Disqus