Introduction
In this article, we will look at some more concepts related to backbone models. We will try to see how we can override the default model behaviour. We will look at the significance of model IDs, how we can validate a model and finally, how a model can be persisted either locally or on a server.
Background
In some of the previous articles, we have discussed the basics of backbone and backbone models. This article is in continuation to that series.
Link to complete series:
- BackBone Tutorial – Part 1: Introduction to Backbone.Js[^]
- BackBone Tutorial – Part 2: Understanding the basics of Backbone Models[^]
- BackBone Tutorial – Part 3: More about Backbone Models[^]
- BackBone Tutorial – Part 4: CRUD Operations on BackboneJs Models using HTTP REST Service[^]
- BackBone Tutorial – Part 5: Understanding Backbone.js Collections[^]
- BackBone Tutorial – Part 6: Understanding Backbone.js Views[^]
- BackBone Tutorial – Part 7: Understanding Backbone.js Routes and History[^]
- BackBone Tutorial – Part 8: Understanding Backbone.js Events[^]
Using the Code
The initialize Function and the Constructor
Whenever we create a model, the backbone will call its initialize
function. We can override this function to provide custom behaviour to it.
var Book = Backbone.Model.extend({
defaults: {
ID: "",
BookName: ""
},
initialize: function () {
console.log('Book has been initialized');
},
});
So when we create this object, the output will be:
Internally, what happens is that whenever a backbone model is created, its constructor gets called. The constructor will call the initialize
function. It is also possible to provide our own constructor and provide the custom behaviour.
var Book = Backbone.Model.extend({
defaults: {
ID: "",
BookName: ""
},
initialize: function () {
console.log('Book has been initialized');
},
constructor: function (attributes, options) {
console.log('Book\'s constructor had been called');
},
});
The output when creating this model:
Now the problem with this constructor is that whenever the backbone model is getting created, our constructor will be called. But the default constructor also does a lot of other activities at the time of object construction like calling the initialize
function. So to make sure that our custom constructor works in unison with all that default behaviour, we need to let the backbone framework know that we still want that default behaviour. This can be done by calling Backbone.Model.apply(this, arguments);
at the end of our custom constructor. This will make sure that our custom constructor will be called and then all the other activities that the default constructor is supposed to do are also done.
var Book = Backbone.Model.extend({
defaults: {
ID: "",
BookName: ""
},
initialize: function () {
console.log('Book has been initialized');
},
constructor: function (attributes, options) {
console.log('Book\'s constructor had been called');
Backbone.Model.apply(this, arguments);
},
});
Now the output will be:
Note: For most practical purposes, overriding the initialize
function will suffice. There is seldom a need to override the constructor but in case one decides to override the constructor, this should be the way to do it.
Model Identifiers - id, cid and idAttribute
Every model needs to be uniquely identified. For this, backbone gives us the model identifiers. The first one to look at is the cid
. The cid
or the client id is auto-generated by backbone so that every model can be uniquely identified on the client.
var book1 = new Book();
var book2 = new Book();
Backbone also provides an identifier id
to uniquely identify the model entity. This is the id that will be used to identify the model when the model data is actually being synced with server, i.e., getting persisted. The cid
is more useful for debugging purposes but the id
attribute will determine the uniqueness of the model when it comes to CRUD operations on the model. It's fairly straight forward to set and get the id
property.
var book2 = new Book();
book2.id = 3;
console.log(book2.id);
Output for the above code will be: 3
.
Now it gets a little confusing at this point. Since most of our models will have an attribute that will correspond to the primary key/unique identifier of the entity, do we need to explicitly set the id
value to that attribute. The answer is yes and no. We have to somehow indicate to the backbone model what attribute should be used as id
but we don't have to set the id
explicitly. We can use the idAttribute
to accomplish this.
var Book = Backbone.Model.extend({
defaults: {
ID: "",
BookName: ""
},
idAttribute: "ID",
initialize: function () {
console.log('Book has been initialized');
},
constructor: function (attributes, options) {
console.log('Book\'s constructor had been called');
Backbone.Model.apply(this, arguments);
},
});
Now in the above code, we have specified that the ID should be used as id
by specifying the idAttribute
. Let's try to create a new model with ID
value now.
var book3 = new Book({ ID: 43 });
console.log(book1.id);
And we can see that the id
value is taken from the specified attribute.
and thus this makes it very easier for the backbone models to work with server side entities and makes the model identification seamless.
Validating the Model
When we are working on business applications, it is often required that we validate the model before persisting the data. Backbone provides a very easy way of validating the model data. We just need to implement the model's validate
function.
var Book = Backbone.Model.extend({
defaults: {
ID: "",
BookName: ""
},
idAttribute: "ID",
initialize: function () {
console.log('Book has been initialized');
},
constructor: function (attributes, options) {
console.log('Book\'s constructor had been called');
Backbone.Model.apply(this, arguments);
},
validate: function (attr) {
if (attr.ID <= 0) {
return "Invalid value for ID supplied."
}
}
});
What happens here is that whenever we try to save the model (which we will see in next section), the Validate
function will get called. It will check the custom validation logic that we have put in place and validate the model. To test the validate
method, we can use model's isValid
function.
var book4 = new Book({ ID: -4 });
var result = book4.isValid();
Another way to prevent the invalid values in the model attributes is by passing the validate:true
while setting the model's attribute. This will also trigger the validate
function.
var book5 = new Book();
book5.set("ID", -1, {validate:true});
What this will do is that this will not even allow setting of invalid values if the value that we are trying to set is invalid as per our custom logic.
How this validation works is that whenever the user chooses to save the model, the validate
function will be called. If there is any validation error, then the model save will fail. Alternatively, the user can choose to pass validate:true
whenever he wants to restrict the setting of invalid values in the model attributes. If we want to check the validity of the model at any particular instance, we can use the isValid
function to test this. Having said that, one important thing to know here is that whenever our validation function fails to validate the model, an event invalid
is raised by backbone. If we want to listen to this event, we can subscribe to this. Let's try to hook up to this event and see the validation errors. We will do this in the initialize
function of the model.
var Book = Backbone.Model.extend({
defaults: {
ID: "",
BookName: ""
},
idAttribute: "ID",
initialize: function () {
console.log('Book has been initialized');
this.on("invalid", function (model, error) {
console.log("Houston, we have a problem: " + error)
});
},
constructor: function (attributes, options) {
console.log('Book\'s constructor had been called');
Backbone.Model.apply(this, arguments);
},
validate: function (attr) {
if (attr.ID <= 0) {
return "Invalid value for ID supplied."
}
}
});
Saving the Model
The backbone models inherently support saving on the server using a restful web API. To save the model using a HTTP REST service, we need to specify the urlRoot
in the backbone model. To actually save the model, we can call the save
on the backbone model. The save
method will trigger the validations and if the validations are successful, it will try to identify the action to be performed, i.e., create or update and based on that action, it will use urlRoot
and call the appropriate REST API to perform the operation.
So if I have a service running on my local machine, I first need to specify the urlRoot
for the service in my model.
var Book = Backbone.Model.extend({
defaults: {
ID: "",
BookName: ""
},
idAttribute: "ID",
initialize: function () {
console.log('Book has been initialized');
this.on("invalid", function (model, error) {
console.log("Houston, we have a problem: " + error)
});
},
constructor: function (attributes, options) {
console.log('Book\'s constructor had been called');
Backbone.Model.apply(this, arguments);
},
validate: function (attr) {
if (attr.ID <= 0) {
return "Invalid value for ID supplied."
}
},
urlRoot: 'http://localhost:51377/api/Books'
});
and to save this model using this service, I could do something like:
var book = new Book({ BookName: "Backbone Book 43" });
book.save({}, {
success: function (model, response, options) {
console.log("The model has been saved to the server");
},
error: function (model, xhr, options) {
console.log("Something went wrong while saving the model");
}
});
The save
function also accepts success and error callback functions so that appropriate action can be taken based on the response from the server.
Now if we want to save the model on local storage rather than on a server, we just need to keep in mind that save function actually calls sync function to actually save/retrieve the model information. So if we need to save the model on a local storage, we need to override the sync function and provide the custom code to save on local storage.
Note: The code shown above (for save
model) is syntactically correct but it will not work unless we have a REST service running at mentioned urlRoot
. In coming articles, I will explain the model save
using the REST service in detail along with the sample service and HTML code.
Point of Interest
So we saw a few more details about the backbone models. We have not yet looked at how to save the model either locally or via using a service. Perhaps in my next articles, we will talk about that only.
History
- 16th July, 2014: First version