Backbone as the Next Step After jQuery
Why Backbone?
Backbone is a lightweight library to organize your code in an object-oriented style. If you're used to getting it done with jQuery, you might sometimes find it hard to maintain the code, when it is anything beyond trivial. Backbone to the rescue!
Integration with jQuery
Backbone depends on jQuery to query and manipulate DOM elements in your app. For many developers that means being able to take very familiar steps to make things happen in your app.
Perhaps you're aware of a good practice to save a jQuery object with DOM element references to a variable, especially if you're going to work with the same set of elements more than once:
var $submenu = $('nav .submenu'); /* <- save the `ul` element, so that the browser doesn't have to do the work of finding it again */
$submenu.toggle(); /* <- use the previously found element */
With Backbone you will go one step further, and use a View object to keep a reference to the DOM element:
var submenu = new Backbone.View({ el: 'nav .submenu' });
submenu.$el.toggle();
There are several things to notice here:
- Use
Backbone.View
prototype to create a new object instanse( How does that work? ) - Pass object with options to the constructor
el
option can be a selector string. Backbone will use jQuery to find the element.- The created
submenu
object has$el
property, which is a jQuery object, so you can call any jQuery methods on it.
Now, why go all the trouble of creating an object with an $el
property, when you could simply create a variable, as in the first example? Because that's when object-oriented programming starts, which will help understand, write and maintain your application so much more.
Backbone.View
You will actually use Backbone.View
to create your own types of elements, that will have their own templates, logic and interaction, shared between all items of a particular view prototype.
var UserView = Backbone.View.extend({});
var joe = new UserView();
joe.$el.appendTo( document.body );
Notice:
- We used
extend
method to create a copy ofBackbone.View
prototype. - We could, but we didn't have to pass
el
option when we created instance of theUserView
- An
$el
property was created for us by Backbone, and contains adiv
element by default. Thediv
DOM element exists only in memory. - We can still use all jQuery methods, which is useful to add the newly created element to the page, in our example.
Right now we have an empty <div></div>
appended to our page, which we can only see by inspecting the page. How do we display anything within that div
?
View: render method
Meet .render
method:
var UserView = Backbone.View.extend({
render: function(){
this.$el.html( '<h1>Hello, World, I am a user of this app</h1>');
return this;
}
});
var joe = new UserView();
joe.render().$el.appendTo( document.body );
Notice:
- We created
render
method, which is used to keep rendering logic. - The "render" name is just a convention, Backbone doesn't call this method for you.
- You can access
$el
property within a prototype method withthis.$el
- You can use any jQuery methods to manipulate the element
- Remember to
return this
in the last line of therender
to make chaining work, something we've all learned to appreciate with jQuery - Lastly remember to actually call
render
, and the template will be rendered within the element.
Backbone.Model
So now we have a template, but how do we pass data there, and have an actual user's name in the template?
Meet Model — where data should be kept and maintained. Model prototype is a blueprint for the data, and model instances are data items that will follow the pattern.
var User = Backbone.Model.extend({
defaults: {
name: '',
age: 0
}
});
var joe = new User({ name: 'Joe', age: 22 });
Notice:
- We created a
User
prototype by extendingBackbone.Model
, just as we did withBackbone.View
. - We used
defaults
property to set default values for properties of the model. - While
defaults
method is not required, it will help document which properties a particular model is going to have, and will define default values for the properties, which will help avoid some bugs. - We create an instance of the
User
model and pass an object with actual data to the constructor.
Object joe
, except for just storing data, includes some useful methods and logic, that will greatly help maintain the data in an application.
Interaction with a model instance.
At the very least, you should use get
and set
methods to read and change the data in your model.
console.log( joe.get('name') );
joe.set({ age: 23 });
- Use
get
method to read a property value. - Pass object with the keys and values that should be changed to the
set
method.
Model and View together
Now that we know what a model looks like, it's time to use it together with a view to render actual data on a page.
Right at this point it is important to remember, that data should never be aware of how it is displayed. We will pass a model object to a view, and the view will own the model object as it's property, will read the data from it, and will be allowed to update the data.
var User = Backbone.Model.extend({});
var UserView = Backbone.View.extend({
render: function(){
this.$el.html('<h1>Hello, I am ' + this.model.get('name') + '</h1>');
return this;
}
});
var joe = new User({ name: 'Joe', age: 22 }); // <-- data is stored here
var joeView = new UserView({ model: joe }); // <-- passing the model to the view
joeView.render().$el.appendTo( document.body ); // <-- render and append to the page
Notice:
- When creating a
UserView
instance, we passedmodel
property, and a model instance as it's value. - That makes the model instance available as
model
property of the view - Just as we use
this.$el
to get to the view's DOM element, we'll usethis.model
to get to its data
Now you might be asking yourself, why did we have to create a model, if we could have just passed a JavaScript object with the same data to the view? Meet Backbone.Events.
Backbone.Events
Backbone.Events prototype is not used directly, but because it is included into every Backbone prototype. It can be used as a publish/subscribe mechanism, a means of communication between Backbone instances.
Simple example:
var joe = new Backbone.Model({ name: 'Joe', age: 22 });
var callback = function(m){
console.log( 'Model changed:', m.toJSON() );
}
joe.on('change', callback); // <- subscribe to any change in the model
joe.set({ age: 23 }); // make a change, and that will fire the callback
Notice:
- You can subscribe to a Backbone event with
on
method, available in any Backbone instance - "change" is one of built-in Backbone events, which is fired when a model's data changes.
- The model is passed to the callback function as an argument
- Model has
toJSON
method, which returns a javascript object with properties and values of the model. That's right, it doesn't really returns a JSON string, but a JavaScript object.
Listening to data change from a view
var User = Backbone.Model.extend({});
var UserView = Backbone.View.extend({
initialize: function(){
this.listenTo( this.model, 'change', this.render );
},
render: function(){
this.$el.html('<h1>Hello, I am ' + this.model.get('name') + '</h1>');
return this;
}
});
var joe = new User({ name: 'Joe', age: 22 });
var joeView = new UserView({ model: joe });
joeView.render().$el.appendTo( document.body );
joe.set({ age: 23 }); // make a change, view will call render
Notice:
initialize
is a special method which is called when an instance of a Backbone prototype is created (exactly where you see thenew
keyword)- Instead of calling
model.on
it is better to useview.listenTo
, which will make sure the listener stays in the object that listens, and not in the one that emits events. That makes it easy to remove a view withremove
method, without having to worry about the listeners. - Whenever "change" event happens in the model instance of that particular view,
render
method is called, which takes care of the up-to-date date being displayed.
Backbone.Collection
Backbone.Collection
is an array on steroids.
Often it makes little sense to create an instance of a model directly from a model. It would be hard to manually create instances of a User
model for every joe
, alice
, carl
and every other user of your application. It's much easier to imagine you would have an array of users.
To easily get models from an array into your application, use Backbone.Collection
.
var User = Backbone.Model.extend({});
var Users = Backbone.Collection.extend({
model: User
});
var users = new Users();
Notice how the User
prototype was passed to the Users
prototype. Now Users
collection knows that it is going to contain items of User
model.
That means we can just add data objects to the Users
collection, and internally Backbone will create instances of User
model.
users.add({ name: 'Joe', age: 22 });
Or add an array of objects:
var users_data = [
{ name: 'Alice', age: 21 },
{ name: 'Carl', age: 31 }
];
users.add( users_data);
Note:
- Mind
Users
vsusers
.Users
is the prototype of the collection, whileusers
is an instance of the collection, which will actually contain data.
Rendering Collection
Backbone.Collection
contains all the usual methods to work with an array, and more. Going through a collection is straightforward with each
method:
users.each(function( user ){
var view = new UserView({ model: user });
view.render().$el.appendTo( document.body );
});
Notice:
- The callback function passed to
each
method will expectedly be called for every item of the collection. - The callback will be called with a model instance as an argument.
- Typically, within the callback function you would create an instance of the respective view, pass current model there, render and append the view to a desired element.
Views and DOM events
It is particularly interesting that it's easy to define DOM event callbacks that will be contained within each view instance.
var UserView = Backbone.View.extend({
render: function(){
this.$el.html('<h1>Hello, I am ' +
this.model.get('name') +
'</h1><span class="close">X</span>');
return this;
},
events: {
'click .close': 'remove'
}
});
Notice:
events
property is special inBackbone.View
. Backbone will look into this property when a new instance of the view is created, and will use it to define callbacks for DOM events that will fire within that particular instance.- Every
Backbone.View
hasremove
method, which will remove the whole element from the screen. - "remove" method is called when you click ".close" element, and only that one instance of the
UserView
will be removed from the screen, others will stay untouched. - Read more about how DOM events and callbacks are defined: backbonejs.org/#View-events
Complete example
See the Pen LNjOmO by Yuriy Linnyk (@yuraji) on CodePen
Cheers
I hope this quick overview have shown you how straightforward it is to get started with Backbone, and how it's simplicity keeps Backbone heard among the most popular frameworks, without major changes over past years. Sometimes all you need is a simple tool that gets the job done without too much behind the scenes magic.
If you would like to learn more, or discuss Backbone and the tooling around it, definitely let me know.
Get the latest and best resources about web development
https://www.hotjs.net/