It’s easy to get started writing a single page JavaScript app – lay out your HTML, add an onload and some onclick event handlers that use XmlHttpRequest (or jQuery.ajax()) to pull data from an API, and update the DOM accordingly. Unfortunately, as your app evolves, this ad-hoc approach can quickly turn into a tangled web of callbacks, JSON wrangling and DOM manipulation so ugly that you wish there was a way to disable ‘view source’ in the browser.

So, you take a leaf from the server-side book (remember Model-View-Controller?) roll up your sleeves and refactor, separating out the data-specific code from the DOM wrangling and realize that someone must have tread this path before, so why reinvent the wheel? That’s when you discover the world of JavaScript MV* frameworks. such as Backbone.js, AngularJS and Ember.js.

When we started building Mobile Packs for the Salesforce Platform, we first looked at Backbone, described on its home page as

[giving] structure to web applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, [connecting] it all to your existing API over a RESTful JSON interface.

Some research on integrating Backbone with Force.com’s REST API led to Piotr Walczyszyn‘s Backbone.Force, a plugin that handily adapts Backbone’s Model and Collection to our purposes, building on the ForceTK library to access the REST API. Many thanks to Piotr for developing Backbone.Force and open sourcing it for others to further build on!

Whether you’re new to Force.com, but familiar with Backbone, or an experienced Force.com developer getting started with client-side frameworks, the Mobile Pack for Backbone.js gives you everything you need to start building a single page JavaScript app leveraging Backbone to synchronize data with Salesforce, and is a great illustration of the power of the MV* approach.

Let’s dive into the app (see the source code for the Visualforce page and equivalent HTML page rendered by a Node.js app). After initializing a ForceTK client and passing it to Backbone.Force, this is all the code you need to set up a model and collection to read Contact records from Salesforce:

//--------------
// Models
//--------------
app.Contact = Backbone.Force.Model.extend({
    type: 'Contact',
    fields: ['Id', 'Name', 'Email', 'FirstName', 'LastName']
});

//--------------
// Collections
//--------------
app.ContactsCollection = Backbone.Force.Collection.extend({
    model: app.Contact,
    query: "WHERE IsDeleted = false" // Use a more selective filter in real life!
});

The sample uses Underscore.js templates to render a view for each contact. We just supply some HTML for the template (note the text/template script type)

<script type="text/template" id="contact-template">
    <% if (typeof(Id) !== 'undefined') { %>
        <a href="#<%= Id %>"><%- Name %></a>
    <% } else { %>
        <%- Name %>
    <% } %>
</script>

and a ContactView with a render() function:

// renders individual Contact list item (li)
app.ContactView = Backbone.View.extend({
    tagName: 'li',
    template: _.template($('#contact-template').html()),
    render: function(){
        this.$el.html(this.template(this.model.toJSON()));
        return this; // enable chained calls
    }
});

Notice how each part of the code has a particular concern, and the View really just marshals the Model’s data into the DOM via the template? That approach is what allows us to build more and more complex apps without the descent into the tangled web of code. We could easily extend the app to, say, show the salutation (Dr, Mrs, etc) for each Contact by just adding that field to the model and template, without having to search through the app to figure out where we query the REST API and how we update the DOM.

The remainder of the sample builds on the above foundation in a very systematic way. There is a View for the Contact list, that knows how to render itself (via a template) as a sequence of ContactView objects, a View for the Contact detail page, that, again, uses a template to render some of the Contact record’s fields and handles the chores of saving and deleting Contact records, and a Router, that handles the ‘plumbing’ between the various Views.

The ContactDetailView save() function is an exercise in structure and minimalism, using the Model to update the record, and navigating back to the Contact list view via the Router:

app.ContactDetailView = Backbone.View.extend({
    //...
    save: function(){
        this.model.save(null, {
            success: function(model) {
                // Setting trigger to true reloads the contact list - we want
                // the latest data from Salesforce!
                app.router.navigate('contacts', {trigger: true});
            },
            error: function () {
                alert('Error saving');
            }
        });
        return false;
    },
    //...
});

Rounding out this quick tour of the sample app, lets look at how the Router plugs a collection and view together with the DOM so that when you request the web page, you see a contact list:

//Define the Application Router
app.Router = Backbone.Router.extend({
    routes: {
        "": "contacts",        // Empty or missing hash fragment
        "contacts":"contacts", // #contacts
        "new": "newContact",   // #new
        ":id": "contact"       // #[record id]
    },
    contacts: function() {
        var contactsCollection = new app.ContactsCollection();
        $.mobile.loading( "show", {
            text: 'Loading Contacts',
            textVisible: true
        });
        contactsCollection.fetch({success: function(){
            $.mobile.loading( "hide" );
            $("#contacts-content").html(new app.ContactsView({
                model: contactsCollection
            }).el);
            // Let jQuery Mobile do its stuff
            $("#contacts-content").trigger( 'create' );
            $.mobile.changePage( "#contacts" , {
                reverse: false,
                changeHash: false
            });
        }});
    },
    // Route handlers for creating and modifying Contact records.
});

By now, you’ll be eager to dive in and get this all working… The easiest way to try out the Backbone Mobile Pack is to follow the quick start for Backbone on Visualforce, or as a standalone Node.js or PHP web app. You can also inspect (fork, modify, etc) the source code in the GitHub repository. One thing you’ll notice if you do dive into the source is that the Mobile Pack currently uses Backbone.js 0.9.2 – Backbone.Force was written long before Backbone turned 1.0. We’ll look at porting it forward to 1.0 soon.

Go ahead and try out the Mobile Pack for Backbone.js, and its counterparts for jQuery Mobile and AngularJS. And let us know (here in the comments) which of the other MV* frameworks you’d like to see us target.

tagged , , , , , , Bookmark the permalink. Trackbacks are closed, but you can post a comment.
  • http://blog.superpat.com/ Pat Patterson

    Answering a comment received via Twitter – the Mobile Packs use ForceTK (https://github.com/developerforce/Force.com-JavaScript-REST-Toolkit) a JavaScript library that wraps the Force.com REST API. The benefit here is simplicity – no server-side code is required, but the cost is consumption of API calls (a limited resource).

    RemoteTK is an alternative library that presents the same JavaScript interface, but uses JavaScript remoting rather than the REST API. This eliminates API call consumption, at the cost of uploading a Visualforce component and Apex class to the org. To switch from ForceTK to RemoteTK, upload the RemoteTK component and class, include the component in your app’s VF page, and change the call to create and set the session id on a ForceTK client to simply create a RemoteTK client. See https://github.com/developerforce/Force.com-JavaScript-REST-Toolkit#using-remotetk-in-a-visualforce-page for more details.

  • Anonymous

    thanks for posting it, its nice write up, is there way to figure out feature intersection of JQ Mobile vs Backbone, so we can make sure not stepping over each other features?

  • Hamayoun Khan

    Pat

    This is very nice. One question: what would it take to add a field for the AccountID of the Contact when editing it? What’s the best way to implement something like the online lookup feature?