Structuring Up Code with Backbone.JS

Hello reader! Hope you’re having a great first month of the year so far!

Things have become pretty  busy lately huhu (but no worries, as there are milestones that hopefully they’ll lead to) that is why this blog post came in 2 days late. Anyhoo, for this blog post, we’ll be looking into Backbone.JS, a JavaScript framework that could give structure to your client side code through the MV* Framework. Backbone.JS relies heavily on the utility functions provided by Underscore.JS (which we saw on our last blog post)  so make sure to have Underscore too when loading Backbone.

Screen Shot 2016-01-15 at 7.48.57 PM.png

MVC and MV* Frameworks

Before we start tinkering with Backbone.JS, let us first talk about MVC frameworks.

Even for at least once, you may have already heard of MVC frameworks, let it be on the client side or the server side. MVC stands for Model, View, and Controller.

Models are like data types that you can mold to represent your entity – maybe a user, a product, a student, etc, that are connected to your data store. Views, on the other hand, define the interface your users would be interacting with. And lastly, controllers, are the middlemen between your model and views – they are the ones responsible for fetching data for display as well as handling user input or actions passed by the views.

Backbone almost follows the same pattern, providing Models and Views, but it does not have a controller. Many consider the view as the controller as well since most of the logic of behaviour lies in it. Some of the entities in Backbone that we will encounter the most include Models, Views, Collections and Routers. Many say that Backbone could still be considered an MVC Framework, but C would stand for Collection which is also heavily used in any Backbone collection. And we’ll see them in a bit.

Prerequisites

Before we look into the components of a Backbone.JS application, let us first identify the things we need to have on hand to create a backbone application.

As mentioned above, we need to have Underscore.JS loaded in our page, so let’s first go and include it.


<script src="underscore.min.js"></script>

There are many ways to load it in your page and and you may refer to this post for ways how. Here I downloaded the Underscore JavaScript to be able to refer to it locally.

Next after we have included underscore, let us now include Backbone itself.

 

<script src="backbone.min.js"></script>

For this, I also downloaded Backbone.JS and included it in my page.

Underscore.JS is a prerequisite of Backbone.JS so the above order matters when loading them.

Backbone Models

So for our first bite of Backbone, let’s look at models.

As mentioned above, models are like data types that you can model to represent entities of your application, like a user, a photo, a product, etc. Creating a model is easy, you just have to extend the Backbone.Model.

myModel = Backbone.Model.extend();
instanceOfMyModel = new myModel();

And then we can try in the console, creating an instance of our model:

Screen Shot 2016-01-16 at 9.21.58 PM.png

From instances of models, we can get attributes through the .get() function. Now, when we tried getting the name attribute of instanceOfMyModel, we got undefined (oops it’s because we haven’t specified it). Let’s try specifying a name attribute upon instantiation:

Screen Shot 2016-01-16 at 9.25.13 PM.png

And tadaaa, we got ‘Victoria’! 🙂

Along with our model’s definition code (where we extended Backbone model), we could also add parameters to further customize our models behaviour. Here are some of the parameters that Backbone allows us to add:

defaults

Class = Backbone.Model.extend({
     defaults: {
           enlisted: false,
     }
});

Math17 = new Class({name: 'Math 17'});
Eng1 = new Class({name: 'Eng 1', enlisted: true})

Screen Shot 2016-01-16 at 9.36.37 PM.png

We see that when we instantiated Math 17 without specifying the enlisted attribute, it was set by default to false. On the other hand, instantiating Eng 1 with a true value for the enlisted attribute, overrode the default value of false.

initialize

The function that you’ll provide to the initialize parameter is ensured to be called everytime an instance of this model is initiated.

Class = Backbone.Model.extend({
     defaults: {
           enlisted: false,
     },
     initialize: function() {
           console.log('Created class: ' + this.get('name'));
     }
});

Math17 = new Class({name: 'Math 17'});

Screen Shot 2016-01-16 at 9.41.50 PM.png

Here, we instructed that for every instantiation, we output some text and it indeed was when we created Math 17.

There are still a whole lot more additional parameters that you can specify and in addition to these, you can also empower your model to have other customized functions:

Class = Backbone.Model.extend({
     defaults: {
           enlisted: false,
     },
     initialize: function() {
           console.log('Created class: ' + this.get('name'));
     },
     drop: function() {
            console.log('Oops, you need to consult your adviser first');
     }
});

Math17 = new Class({name: 'Math 17'});

Screen Shot 2016-01-16 at 9.45.42 PM.png

Collection

The C on MVC won’t be dubbed as Collections for Backbone for no reason. We saw earlier that models are like the blueprint of our data structure. On the other hand, Backbone provides collections to represent groups of our model. For example, a user could belong to an organization, a list item could belong to a todo list, and a class could belong to a curriculum.

Similar to creating a model, collections can also be created by just extending Backbone.Collection:

Curriculum = Backbone.Collection.extend();
CompSciCurri = new Curriculum();
CompSciCurri.length

Screen Shot 2016-01-16 at 9.56.18 PM.png

We can see that the length of our CompSciCurri is still zero as we haven’t created any elements of it yet. Before we add elements, let us first see what other parameters we can provide when extending Backbone.Collection:

model

With model, we can specify the model of our data elements.

Curriculum = Backbone.Collection.extend({model: Class});
CompSciCurri = new Curriculum();

Screen Shot 2016-01-16 at 10.03.39 PM.png

We specified our Curriculum model to be housed by objects with objects with the Class model. We also instantiate a curriculum model and added our first element, Math 17.

create

So far, what we have is all ephemeral data – data is lost upon refresh. Backbone provides capability to save persistent data. We just need to specify where we want data to be stored.

Calling create on a collection instance creates another element of the collection based on the specified model. Trying it out on our current example:

Screen Shot 2016-01-16 at 10.08.51 PM.png

We get an error. Why is this so? Create tries to already store the data we are trying to create but we haven’t specified where yet.

For purposes of demo, we will be storing our data in our browser’s local storage, so be sure to also load the localStorage library in your page for these examples to work. Should you wish to use an API, you can also just provide the url parameter to the definition of your collection.

 

<script src="backbone.min.js"></script>

Now, we adjust our Collection definition to include the use of localStorage.

Curriculum = Backbone.Collection.extend({
     model: Class,
     localStorage: new Backbone.LocalStorage('curri-class')
});

CompSciCurri = new Curriculum();
CompSciCurri.create({name: 'Comm 3', enlisted: true});

Further inspecting what our CompSciCurri looks so far:

Screen Shot 2016-01-16 at 10.22.02 PM.png

Same with models, you may also add additional functions to your collections.

Backbone Views

Now that we already had a glimpse of Models and Collections, let’s now look into Views, where majority of behaviour and interaction logic lies.

Views define what users get to interact with. In Backbone Views, we get to define templates from which our views will be modeled after, define the behaviour when elements receive certain user actions like clicks or key presses, and also even listen to changes and other actions triggered on our models and collections.

For templates, Backbone uses Underscore.JS templating functions which you are more or less already familiar now from our last Underscore blog post. 🙂

Just with models and collections, to create the view we just need to extend the Backbone.View class.

Schedule = Backbone.View.extend();
mySchedule = new Schedule();
mySchedule.tagName
mySchedule.className
mySchedule.el
mySchedule.$el

Screen Shot 2016-01-16 at 11.15.09 PM.png

Wew, it seems that there’s much going on here. But don’t fret, let’s take them one by one. First, we defined Schedule to be a Backbone View.

Schedule = Backbone.View.extend();

And then we created an instance of this Schedule object:

mySchedule = new Schedule();

Now, we have mySchedule. We then checked its innate properties which included tagName, className, el, and $el. Let’s take them one by one:

tagname

Tagname is the container for the element that we are building inside the view. This is required but could be unspecified. If unspecified, it defaults to <div>. That is why from in the preceding screenshot, we got “div”.

Let’s try specifying a tagName:

Schedule = Backbone.View.extend({tagName: 'li'});
mySchedule = new Schedule();
mySchedule.tagName

Screen Shot 2016-01-16 at 11.32.34 PM.png

className

As with tagName, we can also specify any class (and ID), we want our element to have.

Schedule = Backbone.View.extend({tagName: 'li', className: 'highlight'});
mySchedule = new Schedule();
mySchedule.el

Screen Shot 2016-01-16 at 11.38.44 PM.png

There, our produced element is a <li> element with the class highlight. Multiple class names could also be specified by separating them with spaces.

Screen Shot 2016-01-16 at 11.41.05 PM.png

Now, before we proceed on looking at other attributes we can pass along in the View definition. Let’s first talk about $el. $el references the contents of our view. It is the whole element defined by the tagname with the compiled contents inside. In the preceding screenshot, when we got the el attribute from mySchedule, we got the li element that was constructed and getting $el attribute, on the other hand, gives us a jQuery-like array where we can further execute jQuery functions.

Knowing that el is also an attribute, let us try specifying it.

Say we have an existing element in our html page,


&lt;table id='sched-tbl'&gt;

&lt;/table&gt;

We can create a view having our $(‘#sched-tbl’) as its el.

Schedule = Backbone.View.extend({el: $("#sched-tbl"));

Screen Shot 2016-01-17 at 12.10.36 AM.png

And then from there, we can continue to manipulate our el:

Screen Shot 2016-01-17 at 12.10.57 AM.png

Output:Screen Shot 2016-01-17 at 12.11.04 AM.png

Note that what we are using above is the $el attribute of our Schedule object because using el only would just give us the element itself (on which we can’t apply jQuery functions):

Screen Shot 2016-01-17 at 12.22.13 AM.png

Now, let’s get to the additional values that we can define once we instantiate views.

template

With the template parameter, we can specify a specific template we want our view modelled from. It can contain dynamic variables, as well, enclosed in ERB style of evaluation (<%= %>, but could be further customized as we saw with Underscore).

We can specify a simple string

template1 = "I love Math!";
template2 = "I love <%= name %>"

Or get the contents of a text/template we have declared:

template3 = $('#main-div').html()

And then compile it with Underscore’s template,

compiledTemplate1 = _.template(template1)
compiledTemplate2 = _.template(template2)

And then pass our dynamic values:

compiledTemplate(Math17.toJSON())

Screen Shot 2016-01-18 at 1.04.11 AM.png

Screen Shot 2016-01-18 at 1.05.12 AM.png

render

On the other hand, render is the one responsible for rendering and updating the content of our views. Usually this is bind to “on change” events of the model or collection it is responsible for so that the displayed information gets updated.

Schedule = Backbone.View.extend({
   el: $("#sched-tbl"),
   template: _.template('TEMPLATE FOR CONTENTS'),
   render: function() {
       console.log('I am rendering!');
       this.$el.html(this.template());
       return this;
   }
});

mySched = new Schedule();
mySched.render().$el

Screen Shot 2016-01-18 at 1.15.59 AM.png

events

We have mentioned before that views could listen to events made on it. Say it has a Add Button that when clicked, should call a function, or it has an input that when entered, saves the object to the database. And we could do this through the events parameter.

Events accepts a hash with the target events as keys and the functions to be called as values.


events: {

   'click':'alertName',

   'click #add-btn' : 'createProduct',

   'dblClick .label' : 'showInput'

}

In our example above, we declared three events that our view is looking out for.

  • For the first one, we are just declaring that when the whole view is clicked, alertName will be called.
  • The second one specifies that when the #add-btn is clicked, createProduct will be called
  • And the last one just states that when an element with class label is double clicked, showInput is called.

listenTo

In addition to listening to events on elements. Views could also listen to events on our models and collections and by good practice, this is done by using listenTo.


this.listenTo(this.model, 'change', this.render)

Backbone Routers

With the above mentioned features, we can already do a single page application. No need for site refreshes, data is fetched and updated already, realtime. With this, since everything happens in the page,  we may have the same URL all throughout, whatever the state of our page is. Having a constant url though won’t let people bookmark or jump to certain states immediately.

And that’s where backbone routers jump in. Backbone routers provide a way for our url to change and do specific changes when they do.

Here is a sample Backbone Router:

myRouter = Backbone.Router.extend({
    routes: {
       "*param": "doSomething"
    },
doSomething: function(param){
    alert('CALLED'); 
});
 
router1 = new myRouter();
Backbone.history.start();

Here we used a basic *splat, where in *param acts as a sponge to whatever you append to the main url. This parameter could then be passed to doSomething, the method that we specified to be called upon reaching a /#<anything here> URL.

Backbone.history.start() is necessary to let backbone listen to routes and manage URL history.

Now that we have seen the basics, let’s try integrating all of these parts into a sample application.

Sample App:

So now we are going to apply what we have learned so far in a sample application. Say that we have this application that tracks our Travel Plans (cheers to our inner wanderlust! <3)! We can add new places and cross-out already visited ones! We can also filter domestic and international travels as well (sooo, get your passports ready!).

We have the model Trip to represent each of our travel plans and the TripList collection to represent all our Trips. We then create two Views, a TripListView and a TripView, which are collective and individual views for our Trips.

And then finally, we declare a router that would make our “pages” bookmark-able. We’ll be using this for our filters for domestic and international travel as well.

You may also see the full source page in this public gist.

I. Including the required JS files

In the head of our html page, I added jQuery, Underscore, Backbone, and LocalStorage. Take note of the order as one is a prerequisite of the next ones. I also added basic styles to make our page a little bit presentable.

Screen Shot 2016-01-16 at 9.01.25 PM.png

II. Organize Backbone Components

In the spirit of organizing the components of our Backbone application, let us “namespace” them.

Screen Shot 2016-01-16 at 9.03.46 PM.png

III. Individual Trip Model

We first have our Trip model. We utilized Backbone defaults and added a function toggleState that basically just toggles the “completed” attribute of our Trip.

Screen Shot 2016-01-16 at 9.04.03 PM.png

IV. Trips Collection

Next would be our TripList collection which is the group of our Trip models.

Screen Shot 2016-01-16 at 9.04.12 PM.png

V. Defining the template to our individual trips

We first define a template for an individual trip. So basically our trip will be shown in table cells. … and you guessed it right, our tagName for the view that will be utilizing this template is a <tr>.

Screen Shot 2016-01-16 at 9.02.26 PM.png

Once we have defined our template, we then compile it using Underscore’s _.template function and assign it to our Travel.Templates.TripTemplate variable.

Screen Shot 2016-01-16 at 9.04.26 PM.png

Our namespacing has been quite useful so far. One look at a variable, we already know if it’s a model, a collection, a template, and so on.

VI. Individual Trip View

We can notice the _.bindAll(this, ‘render’, ‘toggleCheckbox’, ‘determineClass’) line in the next snippet of code. What we are doing here is ensuring that the correct reference to “this” is used in this methods. We are binding this specific view to this for these methods.

For example, look at the render() function, we can use this.$el. Without binding “this” on the initialize function, we may not be able to do it since this may be bound to something else – a button, the window, or anything that triggered it.

Screen Shot 2016-01-17 at 1.03.43 AM.png

Screen Shot 2016-01-17 at 1.03.58 AM.png

You may also see that in our isHIdden function, we made use of a app.TravelFilter variable which is initialized in our Backbone router (which we will see later).

VII. Trip List View

Next is our trip list view which house all our trips. We can see that it “listens” to the add and filter events on our collection (TripList) and does certain fucntions accordingly (update UI, etc).

Screen Shot 2016-01-17 at 1.04.43 AM.png

Screen Shot 2016-01-17 at 1.04.51 AM.png

And here’s how our $(‘#body’) looks like. It also houses the $(‘#loc-filter’) that when clicked triggers the createTrip function as we indicated through the events hash of our view.

Screen Shot 2016-01-17 at 1.05.28 AM.png

From our $(‘#body’), we can also see $(‘#main-table’) to whom we append the table rows created from our TripListView render function.

$(‘#add-btn’) is present as well and to whom we are on the lookout for the click action as we also specified on the events hash.

VIII. Backbone Router

Screen Shot 2016-01-17 at 1.06.29 AM.png

As we mentioned earlier, it is in the Backbone Router where we set app.TravelFilter in our view’s isHidden function.

This router now allows our URL to change depending on the applied filter:

Screen Shot 2016-01-17 at 1.18.09 AM.png

This way, we can now use the browser’s back button as well as the bookmark button to keep our filters.

Screen Shot 2016-01-17 at 1.15.18 AM.png

IX. Running our App

Screen Shot 2016-01-17 at 1.07.08 AM.png

Demo:

output_X7HVSa.gif

Woo! So much wanderlust feels right now (but that’s alright!). So there, we saw Backbone.JS, its many components, and one of its usage in an application.

Thanks for reading!

References:

P.S. There are still a lot of awesome books and references on Backbone.JS that I haven’t read yet but plan to do soon. For the mean time, on your free time, you may take a look on them as well:

Happy reading!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s