@author: Jeremy Ashkenas (Coffeescript, Underscore.js, Docco)
@website: http://backbonejs.org/
@annotation: http://backbonejs.org/docs/backbone.html
Backbone.js gives structure to web applications by providing models with key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing API over a RESTful JSON interface.
var QUESTIONS = [{
message: 'HTML is ... ?',
type: 'radio',
choices: ['Fun', 'Sexually Transmitted Disease', 'HTML']
}, {
message: 'Who helps decide the outcome of football games',
type: 'radio',
choices: ['God', 'Score', 'Luck']
}, {
message: 'Stormy weather "affects" Cloud Computing',
type: 'radio',
choices: ['sure', 'no', 'seldom']
}, {
message: 'Sun goes around the Earth',
type: 'radio',
choices: ['no', 'it does not', 'yes']
}, {
message: 'Choose Prime Numbers',
type: 'checkbox',
choices: [2, 23, 71, 131, 157, 7, 59, 83]
}];
var QuestionView = Backbone.View.extend({
tagName: 'li',
className: 'question well',
questionTpl: $('#question-tpl').html(),
events: {
'click input': 'complete'
},
initialize: function (question) {
this.model = question;
this.model.attributes.cid = this.model.cid;
this.render();
},
render: function () {
var tpl = _.template(this.questionTpl);
this.$el.html(tpl(this.model.toJSON()));
return this;
},
complete: function (e) {
}
});
<!-- ### Templates ### -->
<!-- Question -->
<script id="question-tpl" type="text/template">
<div class="complete-mark"></div>
<h4><%= message %></h4>
<ul>
<% _.each(choices, function (item, i) {
if (type === 'radio') { %>
<li><label for="<%= cid %>-<%= i %>">
<input type="radio" name="<%= cid %>"
value="<%= item %>"
id="<%= cid %>-<%= i %>">
<div><%= item %></div>
</label></li>
<% } else { %>
<li><label for="<%= cid %>-<%= i %>">
<input type="checkbox" name="<%= cid %>-<%= i %>"
value="<%= item %>"
id="<%= cid %>-<%= i %>">
<div><%= item %></div>
</label></li>
<% }
}); %>
</ul>
</script>
var ListView = Backbone.View.extend({
tagName: 'ul',
className: 'questions',
initialize: function (questions) {
// create new collection of questions.
// turn questions into question model instance
this.collection = new Questions(questions);
// render view
this.render();
},
render: function () {
var $docFragment = $(document.createDocumentFragment());
_.each(this.collection.models, (function (model) {
$docFragment.append(this.renderQuestion(model));
}).bind(this));
// append document frament to View element (e.g. to {tagName: 'li'})
this.$el.append($docFragment);
return this;
},
renderQuestion: function (model) {
var questionView = new QuestionView(model);
return questionView.el;
}
});
var SurveyView = Backbone.View.extend({
id: 'survey-view',
events: {
'click input': 'activateButton'
},
initialize: function () {
this.render();
},
render: function () {
var listView = new ListView(QUESTIONS),
surveyView = this.$el.html(listView.el),
$button = $('<a id="submit" disabled="disabled"\
class="btn btn-success btn-lg"\
href="#thankyou">Submit</a>');
$('#main-view').html(surveyView);
this.$el.append($button);
return this;
},
activateButton: function (e) {
var $questions = $('.question'),
$completedQuestions = $('.question.completed');
if ($questions.length === $completedQuestions.length) {
$('#submit').removeAttr('disabled');
} else {
$('#submit').attr('disabled', 'disabled');
}
}
});
var ThankyouView = Backbone.View.extend({
el: '#main-view',
initialize: function () {
this.render();
},
template: _.template($('#thankyou-tpl').html()),
render: function () {
this.$el.html(this.template());
return this;
}
});
<!-- Thankyou View -->
<script id="thankyou-tpl" type="text/template">
<div id="thankyou-view" class="well">
<h1>Thank You!</h1>
</div>
</script>thankyou.js
in index.html
var SurveyRouter = Backbone.Router.extend({
routes: {
'': 'viewWelcome',
'survey': 'viewSurvey',
'thankyou': 'viewThankyou'
},
viewWelcome: function () {
return new WelcomeView();
},
viewSurvey: function () {
return new SurveyView();
},
viewThankyou: function () {
return new ThankyouView();
}
});
Backbone.history.on('route', function () {
console.log('Route is changed');
}); initialFunction = (function (fn) {
return function () {
// your code here
return fn.apply(this, arguments);
};
})(initialFunction);Example:
var defaultGreeting = function (greeting) {
console.log('Hey!');
console.log(greeting);
};
defaultGreeting = (function (fn) {
return function () {
var newGreeting = arguments[0] + ' Nice to hack you!';
return fn.apply(this, [newGreeting]);
};
})(defaultGreeting);
defaultGreeting('You are awesome!');
Example:
Backbone.View.extend = (function (fn) {
return function () {
var view = arguments[0];
if (view.el && view.el === '#main-view') {
// override template
view.template = _.template('<div id="welcome-view" class="well">\
<h1>Changed by Ninjas!</h1>\
<a id="button-start-survey" class="btn btn-success btn-lg" href="#survey">Start Survey</a>\
</div>');
// add new method
view.newMethod = function () {
alert('New Method added by Ninjas!');
};
// call new method on initialize
view.initialize = (function (fn) {
return function () {
view.newMethod();
return fn.apply(this, arguments);
};
})(view.initialize);
return fn.apply(this, [view]);
} else {
return fn.apply(this, arguments);
}
};
})(Backbone.View.extend);You can override `.initialize` method (e.g. `Backbone.View.prototype.initialize`) in order to make your changes before View/Model is being initialized.
NOTE: It can be done only for the Models/Views that implementation has no `initialize` method re-definition (remains empty)
Example:
Backbone.View.prototype.initialize = (function (fn) {
return function () {
// you awesome javascript
return fn.apply(this, arguments);
};
})(Backbone.View.prototype.initialize);Every time when the route is changed you can listen to `route` event of `Backbone.history` object
NOTE: not every application written in Backbone will support this listener
Example:
Backbone.history.on('route', function () {
// awesome javascript goes here
});You can listen to `hashchange` event and react accordingly when the `window.location.hash` is changing
Example:
$(window).bind('hashchange', function() {
// do something nice
});