Client-side MVC frameworks have gotten increasingly popular with the raise in complexity of in-browser web apps. These frameworks let you organize your JavaScript using the proven MVC pattern. Backbone.js is one of the most popular and is quickly becoming the go-to choice when considering such a framework.

Today we are going to make a service chooser form with Backbone.js, that lets you choose a set of items from a list. A total price field is going to be calculated in real-time with the aggregate price of the services.

Update: We now have a version of this form using AngularJS. See it here.

What is Backbone.js?

Backbone.js is a library that gives structure to web applications by providing models, collections and views, all hooked up together with custom events. It connects your application to your backend via a RESTful JSON interface, and can automatically fetch and save data. In this tutorial we won't be using the advanced features of the library - everything will be stored client-side. Backbone does not replace and does not depend on jQuery, but the two work together nicely.

Backbone won't magically solve your problems though - you still have to be smart in the way you organize your code, which can be an issue if you don't have prior experience with MVC frameworks. Backbone can also be an overkill for smaller applications, where a few lines of jQuery would suffice, so it would better be left for large code bases. The app that we are building here is in the first camp, but it does show the fundamental concepts behind the framework.

The HTML

We are starting off with a regular HTML5 document. I haven't added the HTML5 shim, so this might not look good in older IEs:

index.html

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Your first Backbone.js App | Tutorialzine </title> <!-- Google web fonts --> <link href="http://fonts.googleapis.com/css?family=PT+Sans:400,700" rel='stylesheet' /> <!-- The main CSS file --> <link href="assets/css/style.css" rel="stylesheet" /> </head> <body> <form id="main" method="post" action="submit.php"> <h1>My Services</h1> <ul id="services"> <!-- The services will be inserted here --> </ul> <p id="total">total: <span>$0</span></p> <input type="submit" id="order" value="Order" /> </form> <!-- JavaScript Includes --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone-min.js"></script> <script src="assets/js/script.js"></script> </body> </html>

The main element on the page is the form. The UL inside it will be populated with LI items for the services, and the span inside the #total paragraph will hold the price.

Before the closing </body> tag, I have included jQuery, Backbone and the Underscore library (backbone depends on its powerful utility functions). Last comes the script.js file, which you can see in the next section.

Services Chooser Form With Backbone.js

The JavaScript

Here is the overall idea of our Backbone code:

First we will create a service model. It will have properties for the name of the service, the price, and checked - a status field showing whether this service has been chosen or not. An object of this class will be created for every service that we offer; Then we will create a Backbone collection that will store all the services. It will make it easier to listen for events on all objects at once. In larger apps, you would listen for when items are inserted or removed from the collection, and update the views accordingly. In our case, as the items in the collection are predetermined, we will only listen for the change event (which is raised whenever the checked property is updated). After this, we define a view for the services. Each view will be associated with a single model, and turn its properties into HTML. It listens for clicks and updates the checked property of the model. Lastly, we define a master view, which loops through all the services in the collection and creates views for them. It also listens for the change event on the collection and updates the total price.

Since I know you didn't read all of the above, here is the richly-commented source code:

assets/js/script.js

$(function(){ // Create a model for the services var Service = Backbone.Model.extend({ // Will contain three attributes. // These are their default values defaults:{ title: 'My service', price: 100, checked: false }, // Helper function for checking/unchecking a service toggle: function(){ this.set('checked', !this.get('checked')); } }); // Create a collection of services var ServiceList = Backbone.Collection.extend({ // Will hold objects of the Service model model: Service, // Return an array only with the checked services getChecked: function(){ return this.where({checked:true}); } }); // Prefill the collection with a number of services. var services = new ServiceList([ new Service({ title: 'web development', price: 200}), new Service({ title: 'web design', price: 250}), new Service({ title: 'photography', price: 100}), new Service({ title: 'coffee drinking', price: 10}) // Add more here ]); // This view turns a Service model into HTML. Will create LI elements. var ServiceView = Backbone.View.extend({ tagName: 'li', events:{ 'click': 'toggleService' }, initialize: function(){ // Set up event listeners. The change backbone event // is raised when a property changes (like the checked field) this.listenTo(this.model, 'change', this.render); }, render: function(){ // Create the HTML this.$el.html('<input type="checkbox" value="1" name="' + this.model.get('title') + '" /> ' + this.model.get('title') + '<span>$' + this.model.get('price') + '</span>'); this.$('input').prop('checked', this.model.get('checked')); // Returning the object is a good practice // that makes chaining possible return this; }, toggleService: function(){ this.model.toggle(); } }); // The main view of the application var App = Backbone.View.extend({ // Base the view on an existing element el: $('#main'), initialize: function(){ // Cache these selectors this.total = $('#total span'); this.list = $('#services'); // Listen for the change event on the collection. // This is equivalent to listening on every one of the // service objects in the collection. this.listenTo(services, 'change', this.render); // Create views for every one of the services in the // collection and add them to the page services.each(function(service){ var view = new ServiceView({ model: service }); this.list.append(view.render().el); }, this); // "this" is the context in the callback }, render: function(){ // Calculate the total order amount by agregating // the prices of only the checked elements var total = 0; _.each(services.getChecked(), function(elem){ total += elem.get('price'); }); // Update the total price this.total.text('$'+total); return this; } }); new App(); });

As you can see, you have to extend the classes provided by Backbone, and in the process override the methods that you want to perform differently (for the views you almost certainly want to override the render method). You can further extend them and build hierarchies of classes.

The views can either create their own HTML, as in the case of ServiceView, or be attached to existing elements. The main view of the application is App, which is bound to the #main form. It initializes the other views and updates the total price in its render method.

The PHP

I also included a line of PHP that will handle the form submissions. All it does is print the names of the selected checkbox fields:

submit.php

echo htmlspecialchars(implode(array_keys($_POST), ', '));

You are welcome to extend it with whatever functionality you need, like sending email, inserting the results in a database and more.

We're done!

With this our Backbone service chooser form is done! I hope that you find the form useful and that it gives you a good overview of the framework, so you can tell when the time is right to use it.

Resources and further reading: