Backbone.Model
Backbone models are as flexible as the Views, leaving a lot to the developer. The most basic model you can create is essentially an empty one that extends from Backbone.Model. This doesn’t mean that it does nothing though! It simply means we don’t need to add anything to the features Backbone already offers. We’ll start with an empty model, and then store some data in an instance of it.
var Contact = Backbone.Model.extend({});
var graceHopper = new Contact({
name: ‘Grace Hopper’
});
console.log(graceHopper.get(‘name’));
The above code creates a Model for contacts called ‘Contact’, creates a new Contact variable with an attribute called name. We also demonstrate getting this name attribute out of the model and logging it to console.
Now let’s demonstrate some of the juicier features of Backbone. We’ll add code that listens to changes in the model, and outputs the raw JSON content of the model.
graceHopper.on(‘change’, function() {
console.log(graceHopper.toJSON());
});
graceHopper.set({‘about’: ‘Inventor
of first compiler.’});
Let’s go backwards with this. Notice how easy it is to set a new attribute on our object. You could as easily supply a new name attribute to change it; you could even supply multiple attributes at once.
The first bit of code that calls the on method is adding an event listener, that will call the supplied method when any attribute is added or changed. The method supplied here logs the JSON representation of our data to console using the toJSON method. You can have multiple statements and can even listen to changes in specific attributes if you want. For instance, if you just wanted to know when the name attribute has changed, you can replace the change above with change:name.
Models get even more interesting when server-side data comes into the picture. This is expertly handled by Backbone using just a single attribute: urlRoot. If you have server side data accessible via a RESTful interface that provides JSON data, you’re all set to go with Backbone. Let’s say your server at ‘http:// myserver/’ provides a list of contacts at ‘http://myserver/contacts’ and lets you access an individual contact by id at ‘http:// myserver/contacts/’ you should set url- Root to ‘http://myserver/contacts’.
Once you have set the appropriate urlRoot Backbone can automatically fetch data and put it in the model for you to use. Let’s look at an example:
var Contact = Backbone.Model.extend({
urlRoot: ‘http://myserver/contacts’ });
var contact1 = new Contact({id:11});
contact1.fetch();
This code will fetch the data about the contact with id 11 from the server. You can modify this data and then call contact1.save() to post the changed data back to the server.
Backbone.Collection
Where Model deals with a single data object, a Collection deals with a bulk of data objects. In our case, we can create an Address- Book collection that has a list of contacts. Once we create this Collection we can specify a url attribute that points to the list of contacts on the server, and it will automatically populate the collection with models when you call the fetch method on a Collection instance. You specify which Model this Collection is “collecting” via the model attribute.
Once you have a Collection of Model objects, you can use the where method to search for models matching certain attributes, fetch individual models based on their index in the Collection, or their id on the server. We demonstrate all of this in the following code sample.
var AddressBook = Backbone.Collection({
url: ‘http://myserver/contacts’,
model: Contact
});
var contacts = new AddressBook();
contacts.fetch();
var developers = contacts.
where({‘job’: ‘Developer’});
var person_at_position_11 = contacts.at(11);
var person_with_id_11 = contacts.get(11);;
Backbone.Router
The final piece of the puzzle that we will look at is the Backbone Router. This is the module that makes navigating to different parts of your app work. It can map changes in the state of the app to changes in the URL, so it is possible to bookmark specific locations within the app. For instance, in a contact management application a user might want to bookmark a specific contact and go to it directly rather than needing to navigate to it every time. Here is a sample router for such a contact management application:
var AppRouter = Backbone.Router.extend({
routes: {
‘contacts’, ‘contactsList’,
‘contacts/:id’, ‘contactDetail’
},
contactsList: function() {…},
contactDetail: function(id) {…}
});
var router = new AppRouter;
Backbone.history.start({pushState: true});
router.navigate(‘contacts/11’, {trigger: true});
The routes hash lets you specify different locations within the app, and the method to be called when navigating to that URL. Here, we map the contacts path within the app to the contactsList method, the second path has an :id parameter that is passed to its corresponding method, contactDetail. These methods have been blank here, but you could imagine what they’d do; initialising your models and collections, fetching remote data, and updating the UI.
After creating the router, and instantiating it in the router variable we call the code in Backbone that actually handles all this with the Backbone.history.start call. The pushState parameter set to true allows the browser to use neat urls such as ‘http://myappsite.com/contacts’ instead of hash fragments such as ‘http:// myappsite.com/#contacts’.
The final method call tells the app to go to a specific page within the app, and the trigger parameter set to true simply tells the Router to actually call the corresponding method instead of just changing the URL.
Putting the Pieces Together
You may have noticed that till now that we have used each component almost independently of other components. This is one of the strengths of Backbone. You can use bits and pieces of it in your own app without converting your whole app to it. Just want to use the Model and Collection modules to pull in data, go ahead! Like how Backbone Views work, use them without bothering with the rest. Or use the Router module alone to navigating within your app without bothering with Views, Models or Collections. This isn’t to say these components don’t work well together.
Frameworks like Marionette build on Backbone by extending existing Backbone modules and introducing new ones. For some they can be easier to start with than Backbone.
When creating an instance of a Backbone View, you can pass an instance of a model to bind the model to the view. For example :var contactView = new ContactView({model: modelInstance});.
This model is then accessible inside the view. The view can then listen to changes in the model and update the display on page automatically!
All you will need to listen to the model change events in the initialize method of the View. You can also pull in data from the attached model to display in the view using the toJSON method we have mentioned above. Similarly, you can have Views bound to collections by passing the Collection instance as a parameter just like we have done with model above :
var addressBookView = new Address-
BookView ({collection: myAddress-
Book});
Underscore, a dependency of Backbone includes a template engine that simplifies binding model data to HTML code, but Backbone does not require its use. In fact, it will work with any template engine you want to use, such as Jade or Handlebars.
Conclusion
There is no shortage of JavaScript libraries and frameworks out there that tell you what to do, how to lay out your code, but few like Backbone that just lay back and let you handle things yourself.
This might not make it better, or more suitable for all kinds of projects. In fact, you might find yourself implementing functionality that comes by default with many alternative libraries. However, Backbone is more amenable to use cases that lie slightly outside of the scope or comfort zone of more rigid frameworks.
Backbone can also be extended through plug-ins to support features like, saving to a local storage, integrating with different kinds of services, specialised views and models among other things. If you’re still hankering for more direction, there are frameworks such as Thorax and Marionette build on Backbone and give it a more opinionated direction.