A Little


For Your App
   Luca Mearelli
var Luca = (function(){
    var me = {};
     me.is = 'craftsman';
     me.twitter = '@lmea';
     me.did = ['http://miojob.repubblica.it',
               'http://kiaraservice.com',
               '...'];
    me.uses = function(tool){
         _.indexOf(['ruby', 'rails', 'javascript'], tool)>=0;
     }
    me.used = function(tool){
         _.indexOf(['c++', 'python', 'java'], tool)>=0;
     }
    return me;
})()




                                        @lmea #jsday     http://joind.in/3360
http://github.com/documentcloud/backbone/




                          @lmea #jsday   http://joind.in/3360
A quick poll




           Using DOM libraries?
           Using (end-to-end) frameworks?
           Know or used backbone.js?




                                  @lmea #jsday   http://joind.in/3360
Backbone.js: what is it?




           A lightweight collection of tools
           giving enough structure to help
           building apps in the browser
           without getting in the way



                                @lmea #jsday   http://joind.in/3360
Backbone.js: what is it for?




           Data heavy pages
           Frontends to (JSON) web services
           CRUD-kind web apps




                                @lmea #jsday   http://joind.in/3360
Backbone.js: Model-View-Controller




                                     model




                view                            controller




                                             @lmea #jsday    http://joind.in/3360
Elements of backbone




          Model
          Collection
          View
          Controller


                       @lmea #jsday   http://joind.in/3360
Elements of backbone (2)




          Event
          Sync
          History


                           @lmea #jsday   http://joind.in/3360
Model & Collection




          Representing the data
          Building the business logic
          Handling persistence




                                 @lmea #jsday   http://joind.in/3360
The simplest model




     var Talk = Backbone.Model.extend({})




                                            @lmea #jsday   http://joind.in/3360
Initializing & setting defaults




       var Talk = Backbone.Model.extend({

            // Default attributes for Talk
            defaults: {
               title: "a talk",
               speaker: "me",
               lenght: 60
            },

            intialize: function(){
              if (this.length>10) {
                 this.set({"lightining": false});
               }
            },
       })




                                                    @lmea #jsday   http://joind.in/3360
Assorted CRUD methods




     var my_talk = new Talk({title: "A Little Backbone"});

     my_talk.get("title");
     my_talk.set({"speaker":"Luca Mearelli"});

     if(my_talk.has("video")){
        //...
     };

     my_talk.unset("speaker")
     my_talk.id
     my_talk.cid




                                            @lmea #jsday     http://joind.in/3360
Custom functions




     var Talk = Backbone.Model.extend({

          full_info: function(){
            return this.get("title")+" by:"+this.get("speaker");
          },

          rate: function(stars){
            if(_.isUndefined(this.get("rating"))){
               this.save({rating: stars});
             }
          },

     })




                                               @lmea #jsday    http://joind.in/3360
Validating models



      var Talk = Backbone.Model.extend({

        validate: function(attrs) {
          if (_.isEmpty(attrs.title)) {
            return "The title should be defined";
          }
          if (attrs.lenght>60) {
            return "The maximum lenght is 60 minutes";
          }
        }

      });

      my_talk.bind("error", function(model, error) {
        alert(model.full_info() + " " + error);
      });
      my_talk.set({"lenght":120});




                                             @lmea #jsday   http://joind.in/3360
Saving models




      // delegates to Backbone.sync
      my_talk.save(); // if my_talk.isNew() -> ‘create’ (HTTP “POST”)
      my_talk.save({title:"ahah"}) // otherwise ‘update’ (HTTP “PUT”)

      my_talk.save({...},
                   {
                      success: function(model, resp, xhr){...},
                      error: function(model, errormsg){...}
                   });

      my_talk.url(); // /[collection.url]/[id] or /[urlRoot]/[id]
      Talk.urlRoot = '/talks';




                                             @lmea #jsday    http://joind.in/3360
Backbone.sync




   Backbone.sync = function(method, model, options) {

        switch   (method) {
          case   "read":    ...   break;   //   read !   GET    /collection[/id]
          case   "create": ...    break;   //   create   ! POST    /collection
          case   "update": ...    break;   //   update   ! PUT    /collection/id
          case   "delete": ...    break;   //   delete   ! DELETE    /collection/id
        }

   };

   // to emulate put & delete with post + parameter
   Backbone.emulateHTTP = true;




                                                          @lmea #jsday     http://joind.in/3360
Models collection




      var Track = Backbone.Collection.extend({
        model: Talk
      });

      var track_talks = new Track([talk1, talk2, talk3]);
      track_talks.get(talk2.id); // gets talk2
      track_talks.at(0); // gets talk1
      track_talks.lenght; // is 3




                                             @lmea #jsday   http://joind.in/3360
Collection: adding and removing models




   track_talks.add(
      {title:"JavaScript Survival Guide",speaker:"Giordano Scalzo " }
   );
   track_talks.add([
      {title: "Faster Web Sites 2.0", speaker: "Steve Souders" },
      {title: "HTML5, the current status", speaker:"Patrick H. Lauke"}
   ]);

   track_talks.remove(talk1);
   track_talks.remove([talk2, talk3]);

   track_talks.refresh([
     {title:"Javascript the New Parts",speaker: "Federico Galassi" }
   ]);




                                             @lmea #jsday    http://joind.in/3360
Fetching a collection from the server




      var Track = Backbone.Collection.extend({
        url: function(){
          return "/tracks/"+this.get("number");
        },
        // or simply url: "/talks"
      });

      var track_talks = new Track({number: 1})
      track_talks.fetch(); // calls model.parse(response)




                                             @lmea #jsday   http://joind.in/3360
Custom sorted collections




     track_talks.comparator = function(talk) {
       return talk.get("time");
     }

     track_talks.add(new Talk({time: 17,
                               title: "A Little Backbone"}));

     track_talks.add(new Talk({time: 9,
                               title: "Faster Web Sites 2.0"}));

     track_talks.add(new Talk({time: 10,
                               title: "JavaScript Survival Guide"}));

     alert(track_talks.pluck('title'));

     track_talks.sort();




                                             @lmea #jsday       http://joind.in/3360
Underscore.js goodies



     var Track = Backbone.Collection.extend({

       future: function() {
         return this.filter(function(talk){
                   return todo.get('time')>(new Date()).getHours();
                });
       },

       next: function() {
         return this.first.apply(this, this.future());
       }
       // forEach (each), map, reduce (foldl, inject),
       // reduceRight (foldr), find (detect), filter (select),
       // reject, every (all), some (any), include, invoke, max,
       // min, sortBy, sortedIndex, toArray, size, first, rest,
       // last, without, indexOf, lastIndexOf, isEmpty, chain
     });




                                                @lmea #jsday   http://joind.in/3360
Views




        is a small chunk of the page
        has declarative event handling
        acts like a view controller




                              @lmea #jsday   http://joind.in/3360
A basic view



     // The DOM element for a talk
     var TalkView = Backbone.View.extend({

       //... is a div tag.
       tagName: "div",
       className: "a-talk-row",

       render: function() {
         $(this.el).html(this.model.full_info());
         return this;
       },
     });

     v = new TalkView({
           model:my_talk,
           id: 'talk-'+my_talk.id
         });




                                             @lmea #jsday   http://joind.in/3360
Declarative event handling




     var TalkView = Backbone.View.extend({
       // The DOM events specific to a talk.
       events: {
          "click .title"          : "rateIt",
          "click span.edit"       : "edit",
          "keypress input.title"  : "saveOnEnter"
       },
       edit: function() {
         $(this.el).addClass("editing");
         this.$('input.title').focus();
       },
       saveOnEnter: function(e) { },
       rateIt: function(e) { },

     });




                                             @lmea #jsday   http://joind.in/3360
Sub-view element selection




    view.$(selector) // equivalent to $(selector, view.el)
    this.$(".title").text()
    this.$("li input[@name=email]")
    this.$("div #start")




                                            @lmea #jsday     http://joind.in/3360
Binding model events




    var TrackView = Backbone.View.extend({

       initialize: function() {
         _.bindAll(this, 'addOne', 'addAll', 'render');

          this.collection.bind('add',     this.addOne);
          this.collection.bind('refresh', this.addAll);
          this.collection.bind('all',     this.render);

         this.collection.fetch();
       },

    });

    var current_track = new Track(...);
    var track_view = new TrackView({collection: current_track })




                                              @lmea #jsday   http://joind.in/3360
Rendering templates




    <script type="text/template" id="talk-template">
    <div class="talk <%= lightining ? 'ltalk' : '' %>">
        <div class="talk-title"><%= title %></div>
        <span class="talk-speaker"><%= speaker %></span>
        <% for (i=0;i<rating;i++) { %><img src="star.png" /><% } %>
    </div>
    </script>

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




                                             @lmea #jsday       http://joind.in/3360
Controllers




          intra-page action routing
          evented control flow
          stateful, bookmarkable sub-pages




                                @lmea #jsday   http://joind.in/3360
Controller example




     var ConferenceController = Backbone.Controller.extend({

       routes: {
          "!help":            "help",     // #!help
          "!track/:number":   "search",   // #!track/1
       },

       help: function() {
       },
       search: function(number) {
       }

     });

     new ConferenceController();
     Backbone.history.start();




                                               @lmea #jsday    http://joind.in/3360
Controller routes




     routes: {
       "help/:page":            "help",
       "slides/*path":          "downloadSlides",
       "talk/:track/:talk":     "showTalk",
       "speaker/:name/talks":   "speakerTalks"
       "speaker/:name":         "speaker"
     }

     // Matches #video/10, passing "10"
     this.route("video/:number",
                "video", function(number){ ... });

     // Matches #talk/203/rate/2, passing "203" and "5"
     this.route(/^talk/(.*?)/rate/([1-5])$/,
                "rate", function(id,vote){ ... });




                                               @lmea #jsday   http://joind.in/3360
Backbone.js: demo




          A mobile note taking app
          http://luca.github.com/jsday-demo/




                              @lmea #jsday   http://joind.in/3360
In the end, what does it do?




           JS data models
           Event based
           RESTful (JSON)
           Intra page control flow



                               @lmea #jsday   http://joind.in/3360
And, why should I use it?




           avoids spaghetti code
           cleanly separates concerns
           simple to integrate




                                 @lmea #jsday   http://joind.in/3360
thanks!

http://spazidigitali.com
@lmea

http://www.slideshare.net/lucamea/a-little-backbone-for-your-app



                                       @lmea #jsday      http://joind.in/3360

A Little Backbone For Your App

  • 1.
    A Little For YourApp Luca Mearelli
  • 2.
    var Luca =(function(){ var me = {}; me.is = 'craftsman'; me.twitter = '@lmea'; me.did = ['http://miojob.repubblica.it', 'http://kiaraservice.com', '...']; me.uses = function(tool){ _.indexOf(['ruby', 'rails', 'javascript'], tool)>=0; } me.used = function(tool){ _.indexOf(['c++', 'python', 'java'], tool)>=0; } return me; })() @lmea #jsday http://joind.in/3360
  • 3.
    http://github.com/documentcloud/backbone/ @lmea #jsday http://joind.in/3360
  • 4.
    A quick poll Using DOM libraries? Using (end-to-end) frameworks? Know or used backbone.js? @lmea #jsday http://joind.in/3360
  • 5.
    Backbone.js: what isit? A lightweight collection of tools giving enough structure to help building apps in the browser without getting in the way @lmea #jsday http://joind.in/3360
  • 6.
    Backbone.js: what isit for? Data heavy pages Frontends to (JSON) web services CRUD-kind web apps @lmea #jsday http://joind.in/3360
  • 7.
    Backbone.js: Model-View-Controller model view controller @lmea #jsday http://joind.in/3360
  • 8.
    Elements of backbone Model Collection View Controller @lmea #jsday http://joind.in/3360
  • 9.
    Elements of backbone(2) Event Sync History @lmea #jsday http://joind.in/3360
  • 10.
    Model & Collection Representing the data Building the business logic Handling persistence @lmea #jsday http://joind.in/3360
  • 11.
    The simplest model var Talk = Backbone.Model.extend({}) @lmea #jsday http://joind.in/3360
  • 12.
    Initializing & settingdefaults var Talk = Backbone.Model.extend({ // Default attributes for Talk defaults: { title: "a talk", speaker: "me", lenght: 60 }, intialize: function(){ if (this.length>10) { this.set({"lightining": false}); } }, }) @lmea #jsday http://joind.in/3360
  • 13.
    Assorted CRUD methods var my_talk = new Talk({title: "A Little Backbone"}); my_talk.get("title"); my_talk.set({"speaker":"Luca Mearelli"}); if(my_talk.has("video")){ //... }; my_talk.unset("speaker") my_talk.id my_talk.cid @lmea #jsday http://joind.in/3360
  • 14.
    Custom functions var Talk = Backbone.Model.extend({ full_info: function(){ return this.get("title")+" by:"+this.get("speaker"); }, rate: function(stars){ if(_.isUndefined(this.get("rating"))){ this.save({rating: stars}); } }, }) @lmea #jsday http://joind.in/3360
  • 15.
    Validating models var Talk = Backbone.Model.extend({ validate: function(attrs) { if (_.isEmpty(attrs.title)) { return "The title should be defined"; } if (attrs.lenght>60) { return "The maximum lenght is 60 minutes"; } } }); my_talk.bind("error", function(model, error) { alert(model.full_info() + " " + error); }); my_talk.set({"lenght":120}); @lmea #jsday http://joind.in/3360
  • 16.
    Saving models // delegates to Backbone.sync my_talk.save(); // if my_talk.isNew() -> ‘create’ (HTTP “POST”) my_talk.save({title:"ahah"}) // otherwise ‘update’ (HTTP “PUT”) my_talk.save({...}, { success: function(model, resp, xhr){...}, error: function(model, errormsg){...} }); my_talk.url(); // /[collection.url]/[id] or /[urlRoot]/[id] Talk.urlRoot = '/talks'; @lmea #jsday http://joind.in/3360
  • 17.
    Backbone.sync Backbone.sync = function(method, model, options) { switch (method) { case "read": ... break; // read ! GET /collection[/id] case "create": ... break; // create ! POST /collection case "update": ... break; // update ! PUT /collection/id case "delete": ... break; // delete ! DELETE /collection/id } }; // to emulate put & delete with post + parameter Backbone.emulateHTTP = true; @lmea #jsday http://joind.in/3360
  • 18.
    Models collection var Track = Backbone.Collection.extend({ model: Talk }); var track_talks = new Track([talk1, talk2, talk3]); track_talks.get(talk2.id); // gets talk2 track_talks.at(0); // gets talk1 track_talks.lenght; // is 3 @lmea #jsday http://joind.in/3360
  • 19.
    Collection: adding andremoving models track_talks.add( {title:"JavaScript Survival Guide",speaker:"Giordano Scalzo " } ); track_talks.add([ {title: "Faster Web Sites 2.0", speaker: "Steve Souders" }, {title: "HTML5, the current status", speaker:"Patrick H. Lauke"} ]); track_talks.remove(talk1); track_talks.remove([talk2, talk3]); track_talks.refresh([ {title:"Javascript the New Parts",speaker: "Federico Galassi" } ]); @lmea #jsday http://joind.in/3360
  • 20.
    Fetching a collectionfrom the server var Track = Backbone.Collection.extend({ url: function(){ return "/tracks/"+this.get("number"); }, // or simply url: "/talks" }); var track_talks = new Track({number: 1}) track_talks.fetch(); // calls model.parse(response) @lmea #jsday http://joind.in/3360
  • 21.
    Custom sorted collections track_talks.comparator = function(talk) { return talk.get("time"); } track_talks.add(new Talk({time: 17, title: "A Little Backbone"})); track_talks.add(new Talk({time: 9, title: "Faster Web Sites 2.0"})); track_talks.add(new Talk({time: 10, title: "JavaScript Survival Guide"})); alert(track_talks.pluck('title')); track_talks.sort(); @lmea #jsday http://joind.in/3360
  • 22.
    Underscore.js goodies var Track = Backbone.Collection.extend({ future: function() { return this.filter(function(talk){ return todo.get('time')>(new Date()).getHours(); }); }, next: function() { return this.first.apply(this, this.future()); } // forEach (each), map, reduce (foldl, inject), // reduceRight (foldr), find (detect), filter (select), // reject, every (all), some (any), include, invoke, max, // min, sortBy, sortedIndex, toArray, size, first, rest, // last, without, indexOf, lastIndexOf, isEmpty, chain }); @lmea #jsday http://joind.in/3360
  • 23.
    Views is a small chunk of the page has declarative event handling acts like a view controller @lmea #jsday http://joind.in/3360
  • 24.
    A basic view // The DOM element for a talk var TalkView = Backbone.View.extend({ //... is a div tag. tagName: "div", className: "a-talk-row", render: function() { $(this.el).html(this.model.full_info()); return this; }, }); v = new TalkView({ model:my_talk, id: 'talk-'+my_talk.id }); @lmea #jsday http://joind.in/3360
  • 25.
    Declarative event handling var TalkView = Backbone.View.extend({ // The DOM events specific to a talk. events: { "click .title" : "rateIt", "click span.edit" : "edit", "keypress input.title" : "saveOnEnter" }, edit: function() { $(this.el).addClass("editing"); this.$('input.title').focus(); }, saveOnEnter: function(e) { }, rateIt: function(e) { }, }); @lmea #jsday http://joind.in/3360
  • 26.
    Sub-view element selection view.$(selector) // equivalent to $(selector, view.el) this.$(".title").text() this.$("li input[@name=email]") this.$("div #start") @lmea #jsday http://joind.in/3360
  • 27.
    Binding model events var TrackView = Backbone.View.extend({ initialize: function() { _.bindAll(this, 'addOne', 'addAll', 'render'); this.collection.bind('add', this.addOne); this.collection.bind('refresh', this.addAll); this.collection.bind('all', this.render); this.collection.fetch(); }, }); var current_track = new Track(...); var track_view = new TrackView({collection: current_track }) @lmea #jsday http://joind.in/3360
  • 28.
    Rendering templates <script type="text/template" id="talk-template"> <div class="talk <%= lightining ? 'ltalk' : '' %>"> <div class="talk-title"><%= title %></div> <span class="talk-speaker"><%= speaker %></span> <% for (i=0;i<rating;i++) { %><img src="star.png" /><% } %> </div> </script> render: function() { $(this.el).html(this.template(this.model.toJSON())); return this; }, @lmea #jsday http://joind.in/3360
  • 29.
    Controllers intra-page action routing evented control flow stateful, bookmarkable sub-pages @lmea #jsday http://joind.in/3360
  • 30.
    Controller example var ConferenceController = Backbone.Controller.extend({ routes: { "!help": "help", // #!help "!track/:number": "search", // #!track/1 }, help: function() { }, search: function(number) { } }); new ConferenceController(); Backbone.history.start(); @lmea #jsday http://joind.in/3360
  • 31.
    Controller routes routes: { "help/:page": "help", "slides/*path": "downloadSlides", "talk/:track/:talk": "showTalk", "speaker/:name/talks": "speakerTalks" "speaker/:name": "speaker" } // Matches #video/10, passing "10" this.route("video/:number", "video", function(number){ ... }); // Matches #talk/203/rate/2, passing "203" and "5" this.route(/^talk/(.*?)/rate/([1-5])$/, "rate", function(id,vote){ ... }); @lmea #jsday http://joind.in/3360
  • 32.
    Backbone.js: demo A mobile note taking app http://luca.github.com/jsday-demo/ @lmea #jsday http://joind.in/3360
  • 33.
    In the end,what does it do? JS data models Event based RESTful (JSON) Intra page control flow @lmea #jsday http://joind.in/3360
  • 34.
    And, why shouldI use it? avoids spaghetti code cleanly separates concerns simple to integrate @lmea #jsday http://joind.in/3360
  • 35.