Slide 1

Slide 1 text

Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com The Backbone.js Refactor

Slide 2

Slide 2 text

Daniel Cousineau Developer at SplashMedia @dcousineau

Slide 3

Slide 3 text

intro Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com

Slide 4

Slide 4 text

Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com underscorejs.org

Slide 5

Slide 5 text

Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com Functional, self-contained micro-library

Slide 6

Slide 6 text

+Functional Programming??? Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com Functions are mappings between input and output • They have no side-effects • Input A always results in output b

Slide 7

Slide 7 text

+Functional Programming??? Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com function input output global state global state global state X X X

Slide 8

Slide 8 text

+underscore.js Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com Collections •each •map •reduce •reduceRight •find •filter •reject •all •any •include •invoke •pluck •max •min •sortBy •groupBy •sortedIndex •shuffle •toArray •size Arrays •first •initial •last •rest •compact •flatten •without •union •intersection •difference •uniq •zip •indexOf •lastIndexOf •range Functions •bind •bindAll •memoize •delay •defer •throttle •debounce •once •after •wrap •compose Objects •keys •values •functions •extend •pick •defaults •clone •tap •has •isEqual •isEmpty •isElement •isArray •isObject •isArguments •isFunction •isString •isNumber •isFinite •isBoolean •isDate •isRegExp •isNaN •isNull •isUndefined Utility •noConflict •identity •times •mixin •uniqueId •escape •result •template Chaining •chain •value

Slide 9

Slide 9 text

+map Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com Maps function to each element in the input collection var inp = [1, 2, 3] , out = _.map(inp, function(n){ return n*2; }); //out = [2, 4, 6]

Slide 10

Slide 10 text

+reduce Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com Reduces collection to a single value. mem is the initial state, each successive iteration must be returned var inp = [1, 2, 3]; _(inp).reduce(function(mem, n){ return mem + n; }); //Iter 0: mem = 1 | n = 2 //Iter 1: mem = 3 | n = 3 //Returns: 6

Slide 11

Slide 11 text

+pluck Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com Iterates over a collection and extracts the values for the input key (assumes all elements in the collection are objects/arrays) var stooges = [ {name: 'moe', age: 40} , {name: 'larry', age: 50} , {name: 'curly', age: 60} ]; _.pluck(stooges, 'name'); //Returns ["moe", "larry", "curly"]

Slide 12

Slide 12 text

+min/max Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com Returns the max item in a collection. If second argument (iterator) provided, will use to produce the value to be compared var stooges = [ {name: 'moe', age: 40} , {name: 'larry', age: 50} , {name: 'curly', age: 60} ]; _.max(stooges, function(s){ return s.age; }); //Returns {name: 'curly', age: 60}

Slide 13

Slide 13 text

+keys Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com Returns the keys from a dictionary as an array _.keys({ one: 1, two: 2, three: 3 }); //Returns ["one", "two", "three"]

Slide 14

Slide 14 text

+defaults Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com Maps a duplicate input dictionary on top of a predefined “default” dictionary var iceCream = { flavor: "chocolate" }; _.defaults(iceCream, { flavor: "vanilla" , sprinkles: "lots" }); //Returns {flavor : "chocolate", sprinkles : "lots"}

Slide 15

Slide 15 text

+chaining Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com When chain is initiated, method return a self-reference back to underscore but with the value attached [similar to opening with _(val)]. The chain continues until the value is extracted using .value() var stooges = [ {name: 'curly', age : 25}, {name: 'moe', age : 21}, {name: 'larry', age : 23} ]; var youngest = _.chain(stooges) .sortBy(function(s){ return s.age; }) .map(function(s){ return s.name + ' is ' + s.age; }) .first() .value(); //Returns "moe is 21"

Slide 16

Slide 16 text

Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com backbonejs.org

Slide 17

Slide 17 text

Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com micro-application framework

Slide 18

Slide 18 text

+backbone.js Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com •builds on jquery/Zepto and underscore.js •provides • views • models • collections • router

Slide 19

Slide 19 text

+Model Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com ‣Center of a Backbone Application ‣Provides •Event notification of changes •Ajax support

Slide 20

Slide 20 text

+model Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com var OurModel = Backbone.Model.extend({ defaults: { foo: 'value' , bar: 1 } }); var instance = new OurModel(); instance.on('change:foo', function(modelobj, foo) { console.log('foo is now ' + foo); }); instance.set('foo', 'bat'); //foo is now bat

Slide 21

Slide 21 text

+collection Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com ‣“Collection of models” ‣Convenience event delegation

Slide 22

Slide 22 text

+collection Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com var OurModelCollection = Backbone.Collection.extend({ model: OurModel }); var inst1 = new OurModel() , inst2 = new OurModel() , coll = new OurModelCollection(); coll.on('add', function(modelobj) { console.log('Added model ' + modelobj.cid); }); coll.on('change:bar', function(modelobj, bar) { console.log('Bar for ' + modelobj.cid + ' is now ' + bar); }); coll.add(inst1); coll.add(inst2); inst1.set('bar', 'baz'); //Added model c4 //Added model c5 //Bar for c4 is now baz

Slide 23

Slide 23 text

+view Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com ‣the ‘other center’ of a backbone application ‣provides •abstracted event binding •parent dom element management

Slide 24

Slide 24 text

+view Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com var OurView = Backbone.View.extend({ className: 'our-view' , initialize: function() { this.model.on('change:foo', this.updateFoo, this); } , render: function() { this.updateFoo(); return this; } , updateFoo: function() { this.$el.html(this.model.get('foo')); } }); var modelInst = new OurModel({foo: 'bar'}); , viewInst = new OurView({model: modelInst}); $('#our-container').append(viewInst.render().el); modelInst.set('foo', 'baz'); //
bar
//
baz

Slide 25

Slide 25 text

+router Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com ‣uri matching and view scaffolding

Slide 26

Slide 26 text

+router Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com namespace.MessageCollectionRouter = Backbone.Router.extend({ routes: { "page/:page": "viewPage" } , initialize: function(options){ this.collection = options.collection; } , viewPage: function(page) { this.collection.setPage(page); } , setUrlToPage: function(page) { this.navigate('page/' + page, {trigger:false}); } }); Backbone.history.start({pushState: true, root: '/'});

Slide 27

Slide 27 text

refactor Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com

Slide 28

Slide 28 text

Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com Because you’re not lucky enough right now to start a project from scratch.

Slide 29

Slide 29 text

+first steps Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com

Slide 30

Slide 30 text

+looking conceptually Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com scaffolding DOM Management Event Management State Management Data Synchronization

Slide 31

Slide 31 text

Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com var defaults = {/* ... */}; var methods = { init: function(options) { if (!$(this).is('textarea')) { $.error('Poster must be applied to a textarea'); return undefined; } options = _.defaults(options, defaults); var poster = new Poster(this, options); $(this).data('_poster', poster); return this; } }; $.fn.splash_poster = function(method) { //jQuery Plugin Boilerplate }; function Poster($el, options) { this.$el = $el; this.init(); } Poster.prototype.init = function() { var that = this; this.$el.on('change', jQuery.proxy(this.changeHandler, this)); this.$el.on('keyup', jQuery.proxy(this.changeHandler, this)); }; Poster.prototype.changeHandler = function(e) { //Check character counts, identify urls }; jQuery

Slide 32

Slide 32 text

Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com var defaults = {/* ... */}; var methods = { init: function(options) { if (!$(this).is('textarea')) { $.error('Poster must be applied to a textarea'); return undefined; } options = _.defaults(options, defaults); var poster = new Poster(this, options); $(this).data('_poster', poster); return this; } }; $.fn.splash_poster = function(method) { //jQuery Plugin Boilerplate }; function Poster($el, options) { this.$el = $el; this.init(); } Poster.prototype.init = function() { var that = this; this.$el.on('change', jQuery.proxy(this.changeHandler, this)); this.$el.on('keyup', jQuery.proxy(this.changeHandler, this)); }; Poster.prototype.changeHandler = function(e) { //Check character counts, identify urls }; scaffolding

Slide 33

Slide 33 text

Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com var defaults = {/* ... */}; var methods = { init: function(options) { if (!$(this).is('textarea')) { $.error('Poster must be applied to a textarea'); return undefined; } options = _.defaults(options, defaults); var poster = new Poster(this, options); $(this).data('_poster', poster); return this; } }; $.fn.splash_poster = function(method) { //jQuery Plugin Boilerplate }; function Poster($el, options) { this.$el = $el; this.init(); } Poster.prototype.init = function() { var that = this; this.$el.on('change', jQuery.proxy(this.changeHandler, this)); this.$el.on('keyup', jQuery.proxy(this.changeHandler, this)); }; Poster.prototype.changeHandler = function(e) { //Check character counts, identify urls }; DOM Management

Slide 34

Slide 34 text

Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com var defaults = {/* ... */}; var methods = { init: function(options) { if (!$(this).is('textarea')) { $.error('Poster must be applied to a textarea'); return undefined; } options = _.defaults(options, defaults); var poster = new Poster(this, options); $(this).data('_poster', poster); return this; } }; $.fn.splash_poster = function(method) { //jQuery Plugin Boilerplate }; function Poster($el, options) { this.$el = $el; this.init(); } Poster.prototype.init = function() { var that = this; this.$el.on('change', jQuery.proxy(this.changeHandler, this)); this.$el.on('keyup', jQuery.proxy(this.changeHandler, this)); }; Poster.prototype.changeHandler = function(e) { //Check character counts, identify urls }; Event Management

Slide 35

Slide 35 text

Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com var defaults = {/* ... */}; var methods = { init: function(options) { if (!$(this).is('textarea')) { $.error('Poster must be applied to a textarea'); return undefined; } options = _.defaults(options, defaults); var poster = new Poster(this, options); $(this).data('_poster', poster); return this; } }; $.fn.splash_poster = function(method) { //jQuery Plugin Boilerplate }; function Poster($el, options) { this.$el = $el; this.init(); } Poster.prototype.init = function() { var that = this; this.$el.on('change', jQuery.proxy(this.changeHandler, this)); this.$el.on('keyup', jQuery.proxy(this.changeHandler, this)); }; Poster.prototype.changeHandler = function(e) { //Check character counts, identify urls }; State Management

Slide 36

Slide 36 text

var Poster = Backbone.View.extend({ tagName: 'form' , events: { "change textarea.message": "eChangeBody" , "keyup textarea.message": "eChangeBody" } , initialize: function() { this.render(); this.setModel(this.model); } , setModel: function(model) { this.model = model; this.model.on('change:body', _.bind(this.handleUpdateBody, this)); this.handleUpdateBody(this.model, this.model.get('body')); } , handleUpdateBody: function(model, body) { this.$body.val(body).change(); } , eChangeBody: function(e) { //Check character counts, identify urls } , render: function() { this.$el.append(this._renderBody()); this.$body = this.$('textarea.message'); return this; } }); Backbone.js

Slide 37

Slide 37 text

Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com var Poster = Backbone.View.extend({ tagName: 'form' , events: { "change textarea.message": "eChangeBody" , "keyup textarea.message": "eChangeBody" } , initialize: function() { this.render(); this.setModel(this.model); } , setModel: function(model) { this.model = model; this.model.on('change:body', _.bind(this.handleUpdateBody, this)); this.handleUpdateBody(this.model, this.model.get('body')); } , handleUpdateBody: function(model, body) { this.$body.val(body).change(); } , eChangeBody: function(e) { //Check character counts, identify urls } , render: function() { this.$el.append(this._renderBody()); this.$body = this.$('textarea.message'); return this; } }); scaffolding

Slide 38

Slide 38 text

Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com var Poster = Backbone.View.extend({ tagName: 'form' , events: { "change textarea.message": "eChangeBody" , "keyup textarea.message": "eChangeBody" } , initialize: function() { this.render(); this.setModel(this.model); } , setModel: function(model) { this.model = model; this.model.on('change:body', _.bind(this.handleUpdateBody, this)); this.handleUpdateBody(this.model, this.model.get('body')); } , handleUpdateBody: function(model, body) { this.$body.val(body).change(); } , eChangeBody: function(e) { //Check character counts, identify urls } , render: function() { this.$el.append(this._renderBody()); this.$body = this.$('textarea.message'); return this; } }); dom management

Slide 39

Slide 39 text

Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com var Poster = Backbone.View.extend({ tagName: 'form' , events: { "change textarea.message": "eChangeBody" , "keyup textarea.message": "eChangeBody" } , initialize: function() { this.render(); this.setModel(this.model); } , setModel: function(model) { this.model = model; this.model.on('change:body', _.bind(this.handleUpdateBody, this)); this.handleUpdateBody(this.model, this.model.get('body')); } , handleUpdateBody: function(model, body) { this.$body.val(body).change(); } , eChangeBody: function(e) { //Check character counts, identify urls } , render: function() { this.$el.append(this._renderBody()); this.$body = this.$('textarea.message'); return this; } }); event management

Slide 40

Slide 40 text

Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com var Poster = Backbone.View.extend({ tagName: 'form' , events: { "change textarea.message": "eChangeBody" , "keyup textarea.message": "eChangeBody" } , initialize: function() { this.render(); this.setModel(this.model); } , setModel: function(model) { this.model = model; this.model.on('change:body', _.bind(this.handleUpdateBody, this)); this.handleUpdateBody(this.model, this.model.get('body')); } , handleUpdateBody: function(model, body) { this.$body.val(body).change(); } , eChangeBody: function(e) { //Check character counts, identify urls, update model } , render: function() { this.$el.append(this._renderBody()); this.$body = this.$('textarea.message'); return this; } }); state management

Slide 41

Slide 41 text

+Looking Conceptually Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com widgets Modules Full Suite

Slide 42

Slide 42 text

Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com
$('#widget .item a').click(function(e){ var $this = $(this) , $parent = $this.parent() , id = $parent.data('id'); $.ajax(/* … */); }); //...

Slide 43

Slide 43 text

Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com
$(function(){ var view = new Foo.Bar({el: $('#widget')}).render(); });

Slide 44

Slide 44 text

Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com (function(namespace, $){ namespace.Bar = Backbone.View.extend({ events: { 'click #widget .item a': 'clickItemDelete' } , initialize: function() { } , render: function() { //... return this; } , clickItemDelete: function(e) { var $this = $(e.currentTarget) , $parent = $this.parent() , id = $parent.data('id'); $.ajax(/* … */); } }); })(window.Foo = window.Foo || {}, jQuery);

Slide 45

Slide 45 text

+Backend Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com reuse Backend refactor Backend

Slide 46

Slide 46 text

case study Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com

Slide 47

Slide 47 text

+Case Study Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com

Slide 48

Slide 48 text

+Headline Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com

Slide 49

Slide 49 text

Q& A Put your questions Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com

Slide 50

Slide 50 text

THANKS. FOR YOUR ATTENTION Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com http://joind.in/7993