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.