Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Vertebrae: A Backbone Grabbag

Jake Trent
October 12, 2012

Vertebrae: A Backbone Grabbag

After a time in Backbone, these various bits may help you avoid pitfalls and make some nicer Backbone code.

Jake Trent

October 12, 2012

Transcript

27 slides
  1. MAKE VIEWS REUSABLE - Pass in the “el” attribute var

    MyView = Backbone.View.extend({ }); ! new MyView({ el: ‘#putHere’ }); var MyView = Backbone.View.extend({
 el: ‘#putHere’ }); new MyView();
  2. MAKE VIEWS REUSABLE - Create the container element dynamically var

    MyView = Backbone.View.extend({ tagName: ‘div’ }); ! $(‘#putHere’).html(new MyView({ el: ‘#putHere’ })); var MyView = Backbone.View.extend({
 el: ‘#putHere’ }); new MyView();
  3. ENSURE DOM ELEMENT EXISTS - If DOM is changing, use

    string ‘.selector’ instead of direct reference $(‘.selector’) var MyView = Backbone.View.extend({ }); ! new MyView({ el: ‘#putHere’ }); var MyView = Backbone.View.extend({ }); ! new MyView({ el: $(‘#putHere’) });
  4. ENSURE DOM ELEMENT EXISTS - Backbone will lazy evaluate the

    string ‘el’ // Ensure that the View has a DOM element to render into. // If `this.el` is a string, pass it through `$()`, take the first // matching element, and re-assign it to `el`. Otherwise, create // an element from the `id`, `className` and `tagName` proeprties. _ensureElement : function() { if (!this.el) { var attrs = this.attributes || {}; if (this.id) attrs.id = this.id; if (this.className) attrs['class'] = this.className; this.el = this.make(this.tagName, attrs); } else if (_.isString(this.el)) { this.el = $(this.el).get(0); } } (Backbone source)
  5. CLEANUP YOUR EVENTS - Manually remove events when closing views

    Backbone.View.prototype.close = function(){ this.remove(); // only for views that use tagName this.unbind(); if (this.onClose){ this.onClose(); } }
  6. CLEANUP YOUR EVENTS - Implement an ‘onClose’ function Backbone.View.extend({ onClose:

    function () { this.undelegateEvents(); // if not removing this.collection.off(); // for all event-bound state } });
  7. CLEANUP YOUR EVENTS - this.model.off() only removes .on() events Backbone.View.extend({

    initialize: function () { this.model.fetch({ success: render error: renderError }); } }); Backbone.View.extend({ initialize: function () { this.model.on(‘change’, this.render); this.model.on(‘error’, this.renderError); } });
  8. INHERITANCE - Classic OOP subclassing of your own classes var

    MajesticCreature = Backbone.Model.extend({}); ! var Liger = MajesticCreature.extend({});
  9. INHERITANCE - Extend properties (eg, add additional events) var Place

    = Backbone.View.extend({ events: { ‘ride .scooter’: ‘rideScooterThruPlace’ } }); ! var SecretPlace = Place.extend({ events: _.extend({ ‘fightFor .chips’: ‘beginChipBrawl’ }, Place.prototype.events) });
  10. INHERITANCE - Put different subtypes in a collection // If

    Liger and Nessie extend MajesticCreature ! var Creatures = Backbone.Collection.extend({ model: MajesticCreature, parse: function (res) { var self = this; _.each(res, function (creature) { switch (creature.type) { case ‘skilled-in-magic’: self.add(new Liger(creature)); case ‘underwater-ally’: self.add(new Nessie(creature)); } }); } });
  11. DEVELOP WITHOUT A BACKEND - Store test data.json files locally

    - At the path of the eventual backend service var RipOff = Backbone.Model.extend({ url: ‘/that/place/’ }); ! var theDojoExperience = new RipOff(); theDojoExperience.fetch();
  12. BIND IN INITIALIZE - Guaranteed to run once per object

    var MyView = Backbone.View.extend({ initialize: function () { this.model.on(‘change’, ‘goAndDo’); },
 render: function () { // multiple-run safe } }); var huckabee = new MyView(); huckabee.render(); //ok Huckabee.render(); //ok, just monotonous var MyView = Backbone.View.extend({
 render: function () { this.model.on(‘change’, ‘goAndDo’); } }); var huckabee = new MyView(); huckabee.render(); //ok Huckabee.render(); //bad – goAndDo x 2
  13. SCOPE EVENTS WITH SUBVIEWS - Easily get to model in

    question var ListView = Backbone.View.extend({ // handles fetching collection render: function () { // instantiate model-level views // they ea. render, append to DOM } }); var ModelInListView = Backbone.View.extend({ events: { ‘click .edit’: ‘editModel’ }, editModel: function() { this.model.doEdityThings(); } }); var ListView = Backbone.View.extend({ // handles fetching collection events: { ‘click .model .edit’: ‘editModel’ } render: function () { // render each model in collection in this el }, editModel: function (evt) { var indx = this.$(‘.model).index($ (evt.currentTarget)); this.collection.at(indx).doEdityThings(); } });
  14. COMMUNICATE BETWEEN OBJECTS - Commit to not using globals -

    Unless it’s really global; and then, don’t clutter
  15. COMMUNICATE BETWEEN OBJECTS - Pass reference var QuestionView = Backbone.View.extend({

    initialize: function () { this.answerSheet = this.options.answerSheet; } answerQuestion: function () { var answer = determineAnswer(); this.answerSheet.recordAnswer(answer); } }); ! var AnswerSheet = Backbone.Model.extend({}); ! var myAnswers = new AnswerSheet(); var newQuestion = new Question({ answerSheet: myAnswers });
  16. COMMUNICATE BETWEEN OBJECTS - Talk over global Backbone.Events var QuestionView

    = Backbone.View.extend({ initialize: function () { this.answerSheet = this.options.answerSheet; } answerQuestion: function () { var answer = determineAnswer(); Backbone.Events.trigger(‘answered’, answer); } }); ! var AnswerSheet = Backbone.Model.extend({ initialize: function () { Backbone.Events.on(‘answered’, this.recordAnswer); } }); ! var myAnswers = new AnswerSheet(); var newQuestion = new Question();
  17. MODEL NEWNESS - Model has isNew() function - Not magic:

    isNew: function() { return this.id == null; }
  18. MODEL NEWNESS - Controls HTTP method when calling model.save() -

    isNew(): HTTP POST /model/url/ HTTP PUT /model/url/id - !isNew():
  19. REMEMBER WHO YOU ARE - Manage the fact that ‘this’

    changes - Pass context in event binding Backbone.View.extend({ initialize: function () { this.model.on(‘change’, this.render, this); } render: function () { this.model.getStuffAndMakeHtml() ; // #ftw } }); Backbone.View.extend({ initialize: function () { this.model.on(‘change’, this.render); } render: function () { this.model.getStuffAndMakeHtml() ; // #fail } });
  20. REMEMBER WHO YOU ARE - Capture the context once for

    all your functions Backbone.View.extend({ initialize: function () { _.bindAll(this); this.model.on(‘change’, this.render); } render: function () { this.model.getStuffAndMakeHtml(); // #ftw } });
  21. ORGANIZE YOUR FILES - Use an AMD loader, like RequireJS

    define(function () { return Backbone.Model.extend({}); }); define([‘Luchador’], function (Luchador) { return Backbone.Collection.extend({ model: Luchador }); }); Luchador.js Luchadors.js
  22. MINIMIZE DOM OPERATIONS - They’re expensive. Use a disconnected DOM

    fragment Backbone.View.extend({ render: function () { this.$el.html(‘’); var frag = document.createDocumentFragment(); _(this.collection.models_.each(function (model) { frag.appendChild(createHtml(model)); }); this.$el.html(frag); } }); Backbone.View.extend({ render: function () { var self = this; _(this.collection.models_.each(function (model) { self.$el.append(createHtml(model)); }); } });
  23. BIND IN INITALIZE - This is the verbage - Everyone

    can read this, right? vertebrae = -> alert “super awesome”
  24. jaketrent.com @jaketrent Henry Gray http://en.wikipedia.org/wiki/File:Gray94.png http://www.bartleby.com/107/illus111.html Public Domain ! WebTreats,

    etc. http://webtreatsetc.deviantart.com/art/9-Blue-Striped-Patterns-131303428 Free for personal/commercial ! Jeremy Ashkenas https://github.com/documentcloud/backbone/blob/master/LICENSE MIT license ! Dave Geddes, Jason LuBean, and Matt Werny For their topic suggestions

More Decks by Jake Trent

Other Decks in Technology