Backbone.js – Doing it wrong™
Posted May 21, 2014 | 4 min. (648 words)When working with Backbone.js it’s easy to feel like you are “doing it wrong” sometimes. Although it provides some tools to allow you to better construct your UI code, it doesn’t provide any structure. This is where you or a framework such as Marionette comes in to provide this structure. So how can you tell if you are “doing it wrong?”™. Here are three things that you might spot in your Backbone code.
“Global selectors lead to anger; anger leads to hate; hate leads to suffering” – Master Yoda on jQuery.
This one is easy to spot. If you see your views reaching outside of their own piece of the DOM and altering or interacting with some other piece of the DOM, this is a good sign that you are heading down the wrong path. These jQuery selectors are not scoped to the piece of DOM that this view is bound to. Leading you back towards jQuery spaghetti architecture and suffering.
var MySweetView = Backbone.View.extend({
events: {
'click .button': 'activateLaser'
},
activateLaser: function () {
$('#someOtherElement').hide();
$('#anotherElement').addClass('pew-pew')
}
});
“As you can see, my young apprentice, your code has failed. Now witness the power of this fully modular and reusable UI component!” – Emperor Palpatine on his latest code.
The second thing you might see is when you slightly bind your views to your models. This is generally a perfectly valid method of writing your views and will not cause you any issues. It can hinder you though from producing reusable UI components. Here is a view that provides a button with a confirmation, you can see that it is tightly bound to the specific model with lasers.
var MySweetItemView = Backbone.View.extend({
events: {
'click .button': 'activateLaser'
},
activateLaser: function () {
if (window.confirm('really activate lasers?')) {
this.model.set('laserActivated', true);
}
}
});
One way to avoid this is to bring the idea of a controller and a separate view model into the equation.
var ConfirmView = Backbone.View.extend({
events: {
'click .button': 'confirm'
},
confirm: function () {
if (window.confirm(this.model.get('message'))) {
this.model.set('confirmed', true);
}
}
});
var Controller = {
initialize: function (options) {
this.confirmControl = options.confirmControlModel;
this.lasers = options.laserModel;
this.confirmControl.on('change:confirmed', this.onConfirmation, this);
},
onConfirmation: function () {
this.lasers.set('laserActivated', true);
}
};
You can see that this a lot of extra code, so for simple examples it’s not the best approach. For creating complex reusable UI components though, it is a helpful approach.
“Uh, uh… negative, negative. We have a memory leak here now. Give us a few minutes to lock it down. Large leak, very dangerous.” – Han Solo after building his first Backbone.js application.
It is very easy when using Backbone to create memory leaks. When writing Backbone applications, events are everywhere. They are used in views to bind to DOM elements, in models and collections to respond to changes and also while using Backbone to produce an event driven architecture. The danger comes when using events and failing to unbind them. Using a framework on top of Backbone such as Marionette really helps you with this. It does a lot of unbinding itself and also provides hooks to unbind your other events. The Marionette base view does the following to help with this.
Marionette.View = Backbone.View.extend({
close: function () {
// hook for you to unbind other things
this.triggerMethod("close");
// unbind UI elements
this.unbindUIElements();
// remove the view from the DOM
this.remove();
}
});
“When nine hundred years old you reach, look as good you will not.” – Master Yoda on Backbone.js
Backbone.js may be a bit old hat now, but it is still a useful tool and can be a good way to get your JavaScript under control. You can easily take the wrong steps with it, so hopefully these will help you to not “do it wrong”™. Sounds off in the comments with your own tips1.
- Tips must come with suitable Star Wars quote!