Advertisement

Build a Contacts Manager Using Backbone.js: Part 5

by

This Cyber Monday Tuts+ courses will be reduced to just $3 (usually $15). Don't miss out.

This post is part of a series called Getting to Know Backbone.js.
Build a Contacts Manager Using Backbone.js: Part 4

Welcome back to the Building a content viewer with Backbone series. Over the first four parts, we looked at almost every major component that ships with the latest version of Backbone including models, controllers, views and routers.

In this part of the tutorial, we're going to hook our application up to a web server so that we can store our contacts in a database. We won't be looking at LocalStorage; this is a popular means of persisting the data that Backbone apps use, but the fact is there are already a number of excellent tutorials available on this subject.


Getting Started

We'll need a webserver and a database for this part of the tutorial. I use Microsoft's VWD as an editor, which ships with a built-in web server and works well with MSSQL server, so this is what we'll be using. In truth, it doesn't really matter which stack you decide to go with.

Installing and configuring either of these technologies (VWD and MSSQL server) is beyond the scope of this tutorial, but it's relatively straight-forward to do and there are plenty of good guides out there.

Once installed, you'll want to set up a new database containing a table to store the data in. The table columns should mirror the different properties our models use, so there should be a name column, an address column, etc. The table can be populated with the example data we've used throughout the series so far.

One column that should appear in our new table, but which we haven't used in our local test data is an id, which should be unique to each row in the table. For ease of use, you probably want to set this to auto-increment when the data is added to the table.


Backbone Sync

In order to communicate with the server, Backbone gives us the Sync module; this is the only major module that we haven't used yet and so understanding it will complete our knowledge of the fundamentals of the framework.

Calling the sync() method results in a request being made to the server; by default, it assumes either jQuery or Zepto is in use and delegates the request to whichever of them is present to actually perform. It also assumes a RESTful interface is awaiting on the back-end so by default makes use of POST, PUT, GET, DELETE HTTP methods. As we've seen, Backbone can be configured to fall back to old-school GET and POST methods with additional headers which specify the intended action.

As well as being able to call sync() directly, models and collections also have methods that can be used to communicate with the server; models have the destroy(), fetch(), parse() and save() methods, and collections have fetch() and parse(). The destroy() fetch() and sync() methods all defer to sync() whether being used with models or collections. The parse() method, called automatically whenever data is returned by the server, is by default a simple no-op which just returns the response from the server, but can be overridden if we wish to pre-process the response before consuming it.


Page Load Caveat

The way model data is bootstrapped into the page will vary depending on the back-end technology being used.

The Backbone documentation for the fetch() method (of a collection) states that this method should not be used on the initial page load to request the required models from the server. It goes on to elaborate in the FAQ section that a page should have the required modules already available to the page on load to avoid the initial AJAX request.

This is a great idea and while we don't explicitly have to follow the advice, doing so will make our application just a little bit snappier, and that can only be a good thing.

The way model data is bootstrapped into the page will vary depending on the back-end technology being used. We're going to be using .net in this example, so one way of doing this would be to dynamically create a <script> element containing the required model data, and inject it into the page. To do this we'll need to convert our index.html file to index.aspx instead (we'll also need an index.aspx.cs code-behind or class file too). But doing this raises a new issue.


Using Underscore Microtemplates in an ASPX Page

We can lift the 'Mustache-style' example straight off of the Underscore documentation page.

The problem with Underscore templates is that they use <%= to specify placeholders in the template that are replaced with actual data when the template is consumed. This is the same syntax that ASPX pages use to run dynamic .Net code within HTML tags. The Underscore templates that we've used in this example so far prevent the ASPX page from running correctly and instead it displays a server error.

Fortunately there are several ways around this problem, the simplest way being to change the syntax used to specify the placeholders used in the templates. Underscore exposes the templateSettings property for this very purpose, allowing us to easy specify a regular expression used to match the symbols we wish to use. We can lift the 'Mustache-style' example straight off of the Underscore documentation page in fact; at the start of our app.js file (within the very outer function) we can just add the following code:

_.templateSettings = {
    interpolate: /\{\{(.+?)\}\}/g
};

All this does is supply a new regular expression to the interpolate method, which allows us to use the alternative syntax {{ property }} instead of <%= property %>. We should also at this point go through the templates and change all of the original template tags to use the new syntax.

Although this is not something we've used in our templates so far, there are also additional symbols that Underscore can use. We can evaluate JavaScript using <% and can escape data using <%-. If we wish to use these in our templates and have replaced the interpolate property, we should also configure the evaluate and escape Underscore properties as well.


Bootstrapping the Model Data

We can now think about delivering the model data that is stored in a database to our page when the page is initially rendered. We can easily do this be adding a simple method to the class file for our ASPX page that reads the records from the database and creates a list of objects where each object represents a single contact. We can then serialise the list into a JavaScript array and inject it into the page. As long as the array has the same format as the dummy array we used in the first four parts of this tutorial, we won't have to change our front-end code.

As a placeholder for the array, we can just add a new <script> element to the body of the page, directly before the reference to app.js, which calls the method in the code-behind:

<script>
    var contacts = <%= getData() %>
</script>

The actual logic in the code-behind that performs the database read and list serialisation could vary wildly depending on the implementation, and is somewhat beyond the scope of this tutorial – we're more interested in getting that initial payload on the page than we are about how we actually get it. Feel free to check out the class file in the accompanying code download for probably the quickest and easiest, but by no means the best, way to do it.

At this point, we should be able to remove the contacts array that held our dummy data from app.js, run the page (through the built-in WVD webserver, or IIS) and see exactly the same page, with almost the same functionality, as we saw at the end of part 4. Yay!


Syncing Our App With the Server

In this example, I've used a .net 4.0 ASMX file to handle the requests from the front-end. In order for the back-end to see the data sent to it, we should configure the emulateHTTP and emulateJSON Backbone properties. Add the following lines of code directly after where we changed Underscore's template syntax:

Backbone.emulateHTTP = true;
Backbone.emulateJSON = true;

Whether or not you'll need to configure these properties when building a Backbone app for real depends entirely on the back-end technology you choose to work with.

So, our application could modify the data in several ways; it could change the attributes of a contact that already exists, it could add an entirely new contact, or it could delete a contact that already exists.

The logic to do all of these things on the front-end already exists, but now that a server is involved, the behaviour of the page has already changed. Although the page will render as it did before, if we try to delete a contact, Backbone will throw an error complaining that a url has not been defined. The reason for this is because we used the destroy() method in the deleteContact() method of our ContactView class.

Let's look at how to restore the delete functionality. The first thing we should do then is define a url attribute for our models. Add the property to the Contact class that defines an individual model:

url: function () {
    return "/ContactManager.asmx/ManageContact?id=" + this.get("id");
}

We specify a function as the value of the url property, which returns the URL that should be used to make the requests to. In this example, we can use an asmx web service file to handle the requests. We also add the name of our web method (ManageContact) and add the id of the model as a query string parameter.

Now if we delete one of the contacts when we run the page a POST request is made to the web service. An X-HTTP-Method-Override header is added to the request which specifies that the intended HTTP method was DELETE. We can use this in our web service logic to determine what action to take on the database.

Next we can update the saveEdits() method of the ContactView class so that it notifies the web service when a contact is edited; change the line of code that uses the set() method so that it appears like this:

this.model.set(formData).save();

All we do is chain the save() method on to the set() method. The save() method delegates to the sync() method which makes a POST request to the server. As before the id of the model is sent as a query string and an X-HTTP-Method-Override is used to specify the intended PUT method. This time however, the Content-Type header is set to application/x-www-form-urlencoded (if we didn't configure the emulateJSON property it would be application/json) and the model data is sent as a form data, which we can use to make whatever changes are necessary.

All that is left to do on the front-end is to update the addContact() method of the DirectoryView class. Previously in this method we had an if statement that checked the type of the model being added to see if the select menu needed to be updated. We should now change that if statement so that it appears as follows:

if (_.indexOf(this.getTypes(), formData.type) === -1) {
    this.$el.find("#filter").find("select").remove().end().append(this.createSelect());
}

this.collection.create(formData);

We've trimmed the if statement down to remove the else condition, making the code a bit tidier. We've also removed the add() method and added the create() method in its place. The create() method will actually add the new model to the collection automatically without us manually creating a new instance of our model's class, and it will also make a request to the server, once again delegating to sync().

This time the X-HTTP-Method-Override header does not need to be set, because POST is the method that we would use were the request being made to a RESTful interface anyway. As with the save() method, the model data passed to the create() method is delivered to the server as form data.

As with the server-side code used at the start of this part of the tutorial to bootstrap the initial model data into our app, the code used to process and handle the requests made by Backbone is beyond the scope of the tutorial. We're interested only in the front-end here. As before, the Web service used for this demo is included in the code archive and is fully commented, so check it out if you're interested. I've also included a database backup, which you should be able to restore in order to get going with the demo data.


Summary

In this part of the tutorial, we looked at some of the methods we can use which delegate to Backbone's sync() method in order to communicate with a back-end that can persist the changes made using the front-end of the application.

We saw how Backbone by default makes RESTful requests to a specified URL and how we can configure it in order to work with legacy servers that do not operate on REST principles. We also looked at some of the methods that delegate to sync() in order to communicate with the server. Specifically, we covered the remove(), save() and create() methods and looked at what is sent to the server and how.

We also looked at how easy it is to change the symbols that Underscore uses in order to interpolate data into a template. This now concludes the Contact Manager tutorial; while there are many more features we could add to the application, we have now covered the basics of what it takes to build a fully-functional application using the excellent Backbone.js. Thanks for reading.

Advertisement