Share
Embed
Transcript
50 slides
-
Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com The
Backbone.js Refactor -
Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com Functional,
self-contained micro-library -
+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 -
+Functional Programming??? Daniel Cousineau // follow me : @dcousineau or
http://dcousineau.com function input output global state global state global state X X X -
+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 -
+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] -
+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 -
+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"] -
+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} -
+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"] -
+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"} -
+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" -
+backbone.js Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com
•builds on jquery/Zepto and underscore.js •provides • views • models • collections • router -
+Model Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com
‣Center of a Backbone Application ‣Provides •Event notification of changes •Ajax support -
+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 -
+collection Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com
‣“Collection of models” ‣Convenience event delegation -
+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 -
+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 -
+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'); //<div class="our-view">bar</div> //<div class="our-view">baz</div> -
+router Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com
‣uri matching and view scaffolding -
+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: '/'}); -
Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com Because
you’re not lucky enough right now to start a project from scratch. -
+first steps Daniel Cousineau // follow me : @dcousineau or
http://dcousineau.com <script src="./scripts/jquery-1.9.1.js"></script> <script src="./scripts/underscore.js"></script> <script src="./scripts/backbone.js"></script> <script src="./scripts/jquery-1.9.1.js"></script> -
+looking conceptually Daniel Cousineau // follow me : @dcousineau or
http://dcousineau.com scaffolding DOM Management Event Management State Management Data Synchronization -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
+Looking Conceptually Daniel Cousineau // follow me : @dcousineau or
http://dcousineau.com widgets Modules Full Suite -
Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com <html>
<!-- ... --> <div id="widget"> <ul> <li> <span class="item" data-id="1"> Item 1 <a href="/url/del">Delete</a> </span> </li> <!-- ... --> </ul> </div> <!-- ... --> <script> $('#widget .item a').click(function(e){ var $this = $(this) , $parent = $this.parent() , id = $parent.data('id'); $.ajax(/* … */); }); //... </script> </body> </html> -
Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com <html>
<!-- ... --> <div id="widget"> <ul> <li> <span class="item" data-id="1"> Item 1 <a href="/url/del">Delete</a> </span> </li> <!-- ... --> </ul> </div> <!-- ... --> <script src="./post.js"></script> <script> $(function(){ var view = new Foo.Bar({el: $('#widget')}).render(); }); </script> </body> </html> -
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); -
+Backend Daniel Cousineau // follow me : @dcousineau or http://dcousineau.com
reuse Backend refactor Backend -
case study Daniel Cousineau // follow me : @dcousineau or
http://dcousineau.com -
+Case Study Daniel Cousineau // follow me : @dcousineau or
http://dcousineau.com -
Q& A Put your questions Daniel Cousineau // follow me
: @dcousineau or http://dcousineau.com -
THANKS. FOR YOUR ATTENTION Daniel Cousineau // follow me :
@dcousineau or http://dcousineau.com http://joind.in/7993