Create a gist now

Instantly share code, notes, and snippets.

@asciidisco /webnet.md Secret
Created Oct 19, 2012

What would you like to do?
Data Driven Outline for Backbone.js

In my opinion as a programmer, i´am nearly always able to judge a framework by seeing another programmer building an application based on it. That´s why i decided to do a little live coding presentation for you, explaining the different parts of backbone while building an application.

Step 1

We´re gonna build a small, widget based application, that allows us to turn the LEDs on the arduino on & off. There is a webserver running in the background, with an api for fetching all attached LEDs (using an GET request) & an api to toggle the states of the attached leds (utilitzing a PUT request). Introducing the server api. --> leds.json

Step 2

The application structure is pretty basic. We have an html file with no elements present, loading the needed vendor libraries (jquery/zepto, underscore/lodash & backbone). jq & _ are backbones hard dependencies which can be swapped out in favour of zepto or lodash, that present a similar api. We also load the leds.js, which will hold all the domain specific data for communicating with or led api, this will be our widget file and the app.js, which will contain all our application specific data.

Step 3

We´re gonna start with the essential part, a model representing a LED.

var Led = Backbone.Model.extend({
	url: 'api/led'
});

All backbone components have an extend method handy, that allows us to easily extend them without fiddling around with the prototype. To do smth. useful in our situation, the model needs also an url property to know with which api endpoint it´s supposed to communicate.

Step 4

We do not only have one LED attached, we have three of em, so as learned before, we´re going to use a 'Backbone.Collection' to fetch & store all of the LED entities.

var LedList = Backbone.Collection.extend({
	url: 'api/leds',
	model: Led
});

The collection also has an url property, so it can fetch the complete list of LEDs. For each array entry in our json response, it will create an Led Model instance (that´s why whe have this 'model' property hanging around) & stores it in his own "array on steroids" manner.

Step 5

Don´t be nervous guys, we´ll be getting closer to our first results. Chunks of HTML, or DOM elements at all are represented as a Backbone.View instance. We need two, to fit our needs, one that takes care of wrapping the list of LEDs, and a snd. one that will represent a LED (Wow, you see the Collection, Model relation and how we mdoel our views after the incoming data) ;)

var LedListView = Backbone.View.extend({
	tagName: 'ul',
	id: 'leds',
	render: function () {
		return this.$el;
	}
});

Normally backbone creates a 'div' as the std. element for every view, with tagName its possible to override this behaviour. Also, the id you see here will be represented as an actual attribute on the dom element.

Step 6

So, now that we´re able to generate DOM elements, lets build the actual DOM representation of our LED

var LedView = Backbone.View.extend({
	tagName: 'li',
	className: 'led',
	render: function () {
		return this.$el;
	}
});

Basically the same as the LED list view, but instead of an id, we´re attached a classname.

Step 7

Okay, we can fetch Data, we can generate DOM Elements, basically we´re done with the basic widget stuff, lets glue this all together in our application aka. app.js

But, before we do that lets export our widget to the global namespace

window.LedModule = {
	Led: Led,
	LedList: LedList,
	LedListView: LedListView,
	LedView: LedView
};

Okay, now that we can access our Objects, lets play with em:

First initialize the LED list collection

var leds = new LedModule.LedList();

Then we´re going to initialize the List View

var ledsView = new LedModule.LedListView({});

Lets render & inject our list view:

ledsView.render().appendTo($('body'));

Now we see, that we have an 'ul' element created in our DOM Tree.

In the end, we need to fetch the Data:

leds.fetch({add: true});

Fetch fires up an AJAX call to our server, the "add:true" flag tells backbone to fire an event, for every entity in the collection. When we bind a callback to that collection add event, we´re able to generate a view instance for every LED entity in the servers response & attach it to the DOM, or more precisly to the LED list view.

leds.bind('add', function (led) {
	new LedModule.LedView({
		model: led
	}).render().appendTo(ledsView.$el);
});
initialize: function (options) {
	_.bindAll(this, 'render');
},

In every backbone object, the initialize method acts like a constructor & all "non magic backbone properties" that we´re injecting will end up in the 'options' argument. We also need to take care of the scope of the render method, so a thing that you often experience is the scope change of a method, with underscores 'bindAll' method, we can prevent this form happening.

Step 8

Okay, we now have a list with empty 'li' elements, thats cool, but nirt quite what we want. So lets add a little bit of content, using the underscore templating feature.

<script type="text/template" id="tmpl-led">
	<span class="indicator <%= color %> <%= state === true ? 'on' : '' %>"><%= pin %></span>
</script>

These templates are like are semanticly similiar to ruby erb templates & you can just open a tag & write normal javascript. Now were going to fetch the template & stick the contents it into our li element.

var ledTpl = _.template($('#tmpl-led').html());
leds.bind('add', function (led) {
	new LedModule.LedView({
		model: led,
		template: ledTpl
	}).render().appendTo(ledsView.$el);
});
initialize: function (options) {
	_.bindAll(this, 'render');
	this.template = options.template;
}

We´re then going to store the template & changing or render method to use it.

render: function () {
	return this.$el.append(this.template(this.model.toJSON()));
}

Okay, we´re serialized the models properties, rendered the template & appended it to our main views element.

Step 9

Now that we have a visual representation, lets add some interactivity. We bind a 'click' handler to the li element.

events: {
	'click .indicator': 'toggleLed'
}

Write a a 'toggleLed' method that fires an event:

toggleLed: function (event) {
	this.model.trigger('toggle');
	return this;
}

Bind the context of that method too

_.bindAll(this, 'render', 'toggleLed');

Now we need to create the 'toggle' event in our LED model

initialize: function () {
	this.on('toggle', this.toggleState);
}

And finally write the method & make the call to our server, to toggle the 'real' LEDs state

toggleState: function () {
	return this.set('state', !this.get('state')).save();
}

Cool, but it would be even better to see the change reflected in our UI: So, we´re going to write a method in our view to do that:

toggleState: function (model, state) {
	this.$el.find(':first-child').toggleClass('on', state);
	return this;
}

So this toggles our css class 'on' that we´re setting on the LED element Last thing we need to do, is to bind that event to our models 'state:change' event. These kind of events will always be fired when a model property changes.

initialize: function (options) {
	this.template = options.template;
	// fix issues with the loss of this
	_.bindAll(this, 'render', 'toggleLed', 'toggleState');
	// bind the 'toggleColor' function to the state change event of the model
	this.model.bind('change:state', this.toggleState);
}

Step 10

We´re nearly done, all we wanna add is a sorting of our collection, to display the leds based on their 'pin' property, thats easy as stealing sweets from a baby by adding a comparator method:

comparator: function (model) {
	return model.get('pin');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment