6 min read

In this Backbone tutorial from Andrew Burgess, author of Backbone.js Blueprints, we’ll be looking at two key extensions that you can use when working with models and collections. As you will see, both give more rigidity to what is an incredibly flexible framework. This can be extremely valuable when you want a reliable way to perform certain front-end web development tasks.

Relations In Backbone

Backbone is an extremely flexible front-end framework. It is very unopinionated, as frameworks go, and can be bent to build anything you want. However, for some things you want your framework to be a little more opinionated and give you a reliable way to perfrom some operation.

One of those things is relating models and collections. Sure, a collection is a group of models; but what if you want to relate them the other way: what if you want a model to have a “child” collection? You could role your own implementation of model associations, or you could use one of the Backbone extension libraries made expressly for this purpose. In this article, we’ll look at two extension options: Backbone Associations and Backbone Relational.

Backbone Associations

We’ll use the example of an employer with a collection of employees. In plain Backbone, you might have an Employer model and an Employees collection, but how can we relate an Employer instance to an Employees instance? For starters, let’s create the Employee model:

var Employee = Backbone.Model.extend({});

Notice that we are extending the regular Backbone.Model, not a special “class” that Backbone Associations gives us. However, we’ll use a special class that next:

var Employer = Backbone.AssociatedModel.extend({
    relations: [{
        type: Backbone.Many,
        key: 'employees',
        relatedModel: Employee
    }]
});

The Employer will be an extention of Backbone.AssociatedModel class. We give it a special property: relations. It’s an array, because a model can have multiple associations; but we’ll just give it one for now. There are several properties that we could give a relation object, but only three are required. The first is type: it must be either Backbone.Many (if we are creating a 1:N relation) or Backbone.One (if we are creating a 1:1 relation). The second required parameter is key, which is the name of the property that will appear as the collection on the model instance. Finally, we have the relatedModel, which is a reference to the model class.

Now, we can create Employee instances.

var john = new Employee({ name: "John" }),
    jane = new Employee({ name: "Jane" }),
    paul = new Employee({ name: "Paul" }),
    kate = new Employee({ name: "Kate" });

Then, we can create an Employer instance and relate the Employee instances to it:

var boss = new Employer({ 
    name: 'Winston', 
    employees: [john, jane, paul, kate]
});

Notice that we’ve used the special relation key name employees. Even though we’ve assigned a regular array, it will be converted to a full Backbone.Collection object behind the scenes. This is great, because now you can use collection-specific functions, like this:

boss.get('employees').pluck('name'); // ['John', 'Jane', 'Paul', 'Kate']

We can even use some special syntax with the get method to get properties on our nested models:

boss.get('employees[0].name'); // 'John'
boss.get('employees[3].name'); // 'Kate'

Unfortunately, relations with Backbone Associations are a one-way thing: there’s no relation going from Employee back to Employer. We can, of course, set an attribute on our model instances:

john.set({ employer: boss });

But there’s nothing special about this. We can make it an association, however, if we change our Employee from a regular Backbone.Model class to a Backbone.AssociatedModel class.

var Employee = Backbone.AssociatedModel.extend({
    relations: [{
        type: Backbone.One,
        key: 'employer',
        relatedModel: 'Employer'
    }]
});

Two things are different about this relation. First, it’s a one-to-one (1:1) relation, so we use the type Backbone.One. Second, we make the relatedModel a string, instead of a reference to the Employer object; this is necessary because Employer comes after Employee in our code, and hence can’t be references directly as this point; model class will look it up later, when it’s needed. Now, we’ll still have to set our employer attribute on Employee models, like so:

john.set({ employer: boss });

The difference now is that we can use the features Backbone Associations provides us with, like nested getting:

john.get('employer.name'); // Winston

One more thing: I mentioned that the array attribute with the special key name becomes a collection object. By default, it’s a generic Backbone.Collection. However, if you want to make your own collection class with some special features, you can add a collectionType property to the relation:

var EmployeeList = Backbone.Collection.extend({
    model: Employee    
});

var Employer = Backbone.AssociatedModel.extend({
    relations: [{
        type: Backbone.Many,
        collectionType: EmployeeList
        key: 'employees',
        relatedModel: Employee
    }]
});

Backbone Relational

Backbone Relational has very similar syntax to Backbone Associations; however, I prefer this library because it makes our relations two-way affairs from the beginning. First, both models are required to extend Backbone.RelationalModel.

var Employee = Backbone.RelationalModel.extend({});

var EmployeeList = Backbone.Collection.extend({
    model: Employee    
});

var Employer = Backbone.RelationalModel.extend({ 
    relations: [{
        type: Backbone.HasMany,
        key: 'employees',
        relatedModel: Employee,
        collectionType: EmployeeList
        reverseRelation: {
            key: 'employer' 
        }
    }]
});

Notice that our Employer class has a relations attributes. The key, relatedModel, and type attributes are required and perform the same duties their Backbone Associated counterparts do. Optionally, the collectionType property is also avaiable.

The big difference with Backbone Relational—and the reason I prefer it—is because of the reverseRelation property: we can use this to make the relationship act two ways. We’re giving it a single property here, a key: this value will be the attribute given to model instances on the other side of the relationship. In this case, it means that Employee model instances will have an employer attribute.

We can see this in action if we create our employer and employees, just as we did before:

var john = new Employee({ name: "John" }),
    jane = new Employee({ name: "Jane" }),
    paul = new Employee({ name: "Paul" }),
    kate = new Employee({ name: "Kate" });

var boss = new Employer({ 
    name: 'Winston', 
    employees: [john, jane, paul, kate]
});

And now, we have a two-way relationship. Based on the above code, this part should be obvious:

boss.get('employees').pluck('name'); // ['John', 'Jane', 'Paul', 'Kate']

But we can also do this:

john.get('employer').get('name'); // Winston

Even though we never gave the john instance an employer attribute, Backbone Relational did it for us, because we gave an object on one side of the relationship knowledge of the connection. We could have done it the other way, as well, by giving the employees an employer:

var boss = new Employer({ name: 'Winston' });

var john = new Employee({ name: "John", employer: boss }),
    jane = new Employee({ name: "Jane", employer: boss }),
    paul = new Employee({ name: "Paul", employer: boss }),
    kate = new Employee({ name: "Kate", employer: boss });

boss.get('employees').pluck('name'); // ['John', 'Jane', 'Paul', 'Kate']

It’s this immediate two-way connection that makes me prefer Backbone Relational. But both libraries have other great features, so check them out in full before making a decision.

I’ve found that the best way to get better at using Backbone is to really understand what’s going on behind the curtain, to get a feel for how Backbone “thinks.” With this in mind, I wrote Backbone.js Blueprints. As you build seven different web applications, you’ll learn how to use and abuse Backbone to the max.

About the Author

Andrew Burgess is a primarily JavaScript developer, but he dabbles in as many languages as he can find. He’s written several eBooks on Git, JavaScript, and PHP, as well as Backbone.js Blueprints, published by Packt Publishing. He’s also an web development instructor as Tuts+, where he produces videos on everything from JavaScript to the command line.

LEAVE A REPLY

Please enter your comment!
Please enter your name here