BACKBONE.JS
Javascript framework intro




                        // based on Artjom Kurapov’s experience on
                                               Weekendee project
Problem
        (JS) code becomes messy
    Dynamic HTML (done any dynamic tables with jquery?)
Lots of 0-level UI functions
Solution
Object Oriented code = more focus on what, less on function
Higher level of abstraction = more layers
Enter JS frameworks
              Hello ladies..
Details please
   View — Collection — Model ( MVC, right? )
   Lightweight (6kb ≠ Extjs 1mb)
   JSON, pure REST (remember PUT/DELETE http headers?)
   Event driven (bang your head here)
SO its not a
 DOM manipulation lib (jquery, prototype, mootools)
 Animation kit
 Control suite (jquery UI, ExtJS)
Core Concepts
   Models — storage
   Collections — grouping
   Views — rendering
   Router — navigation
Bird’s eye view
Models
Primitive model
Now can be used as
me.set({someAttr:’Today!’});
me.save();


… but we can handle saving too …
.save({success:.., error:..});


… and tie it to some view …
SomeEventView.model
Actual model definition
Weekendee.Models.Event = Backbone.Model.extend({

      defaults: {
      },

      url: function () {
          if (this.id === undefined) this.id = '';
          return base_url + 'event/' + this.id;
      },

      validate: function (attrs) {
          if (attrs.text != null && $.trim(attrs.text) == '') {
               return t('event.error_empty');
          }
      }
});
Kohana controller in back
  public function action_index() {
            $requestMethod = $this->request->method();

            switch ($requestMethod) {
                      case 'GET':
                                 $eventId = $this->request->param('id');
                                 $this->_read();
                                 break;

                      case 'POST':

                                 $eventId = $this->request->param('id');

                                 if (!$eventId) {
                                           $this->_create();
                                 }
                                 else if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
                                           $httpMethodOverride = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
                                           if ($httpMethodOverride == "PUT") {
                                                      $this->_update();
                                           }
                                           else if ($httpMethodOverride == "DELETE") {
                                                      $this->_delete();
                                           }
                                 }

                                 break;
                      default:
                                 $this->_read();
            }
  }
Views
Primitive view
View events
Views – most important
   .extend
   events:{}
   .el
   .initialize() -> .render() -> .template()
_js templating
Actual view definition
Weekendee.Views.EventComment = Backbone.View.extend({
     className: 'comment clearAfter’,
     template: _.template($('#event-comment-template').html()),
     events: {'click a.removeComment': 'removeComment’},
     render: function () {
          var template_vars = this.model.toJSON();
          template_vars.text = replaceURLWithHTMLLinks(template_vars.comment);
          $(this.el).html(this.template(template_vars));
          $(this.el).attr('data-id', this.model.get('commentId'));
          $('h3', $(this.el)).css('color', this.model.get('color'));
          return this;
     },
     removeComment: function () {
          this.model.destroy();
          this.remove();
     }
});
Collections
Primitive collections
Actual collection definition
Weekendee.Collections.FriendRequests = Backbone.Collection.extend({
    page: 1,

      url: function () {
          return base_url + 'user/list_friend_requests/?page=' + this.page;
      },

      parse: function (resp) {
          this.page = resp.page;
          this.perPage = resp.per_page;
          this.total = resp.total;
          return resp.models;
      }
});
Actual collection creation
Weekendee.Views.ContactsPage = Backbone.View.extend({
    "el": "#contacts",
    show: function () {
         this.friendRequestsView = new Weekendee.Views.FriendRequests({
                  collection: new Weekendee.Collections.FriendRequests()
             });
…
Actual collection view
Weekendee.Views.FriendRequests = Backbone.View.extend({
     'el': '#friend_requests’,
     initialize: function () {
          this.collection.bind('reset', this.reset, this);
          this.collection.fetch();
     },
     reset: function (FriendRequestWrapper) {
          var me = this;
          $('.contacts-body', $(this.el)).html('');

          $.each(FriendRequestWrapper.models, function (i, model) {
               var view = new Weekendee.Views.FriendRequest({
                    model: new Weekendee.Models.Contact(model)
               });
               $('.contacts-body', $(me.el)).append(view.render().el);
          });
      }
});
Router
Primitive router
Actual router
Weekendee.Routers.AppRouter = Backbone.Router.extend({
      routes: {
            "/week": "week", // #/week
            "/": "week",
            "": "week",
            "/contacts": "contacts", // #/week
            //"/week/:year/:week": "week",// #/week/2011/13
            //"/week/:startdate": "week", // #/week/2011-10-07
            "/event/:id": "event", // #/event/123
            "/event/:id/:uid": "event", // external facebook links
            "/vibe/:id": "vibe", // #/vibe/123
            "/invite/:id": "invite", // #/vibe/123
            "/*": "initial”
      },
      week: function () {
            Weekendee.WeekPage.reload();
            Weekendee.WeekPage.show();
            //Weekendee.WeekPage.setSlider(sliderState);
            Weekendee.ContactsPage.hide();
      },
Mine field
Problems
 Event propagation - loss of bind context
var me = this;
var comment = new Weekendee.Models.EventComment(commentModel);
me.collection.add(comment);
comment.bind('destroy', me.removeComment,   me);


 Hard to debug if you’re being dumb – model is
  not printable
  userModel.set(‘name’,’Dr. Who?’);
Problems
 Lack of documentation / best practices…
Problems
 (because of) big variety of connectivity
  combinations:
  X Models (update/get updated by)
  Y Views with
  Z Collections
 And thus.. How to bind stuff correctly?
 Manage collection of views?

Bacbkone js

  • 1.
    BACKBONE.JS Javascript framework intro // based on Artjom Kurapov’s experience on Weekendee project
  • 2.
    Problem (JS) code becomes messy Dynamic HTML (done any dynamic tables with jquery?)
  • 3.
    Lots of 0-levelUI functions
  • 4.
    Solution Object Oriented code= more focus on what, less on function Higher level of abstraction = more layers
  • 5.
    Enter JS frameworks Hello ladies..
  • 6.
    Details please  View — Collection — Model ( MVC, right? )  Lightweight (6kb ≠ Extjs 1mb)  JSON, pure REST (remember PUT/DELETE http headers?)  Event driven (bang your head here)
  • 7.
    SO its nota  DOM manipulation lib (jquery, prototype, mootools)  Animation kit  Control suite (jquery UI, ExtJS)
  • 8.
    Core Concepts  Models — storage  Collections — grouping  Views — rendering  Router — navigation
  • 9.
  • 10.
  • 11.
  • 12.
    Now can beused as me.set({someAttr:’Today!’}); me.save(); … but we can handle saving too … .save({success:.., error:..}); … and tie it to some view … SomeEventView.model
  • 13.
    Actual model definition Weekendee.Models.Event= Backbone.Model.extend({ defaults: { }, url: function () { if (this.id === undefined) this.id = ''; return base_url + 'event/' + this.id; }, validate: function (attrs) { if (attrs.text != null && $.trim(attrs.text) == '') { return t('event.error_empty'); } } });
  • 14.
    Kohana controller inback public function action_index() { $requestMethod = $this->request->method(); switch ($requestMethod) { case 'GET': $eventId = $this->request->param('id'); $this->_read(); break; case 'POST': $eventId = $this->request->param('id'); if (!$eventId) { $this->_create(); } else if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) { $httpMethodOverride = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']; if ($httpMethodOverride == "PUT") { $this->_update(); } else if ($httpMethodOverride == "DELETE") { $this->_delete(); } } break; default: $this->_read(); } }
  • 15.
  • 16.
  • 17.
  • 18.
    Views – mostimportant  .extend  events:{}  .el  .initialize() -> .render() -> .template()
  • 19.
  • 20.
    Actual view definition Weekendee.Views.EventComment= Backbone.View.extend({ className: 'comment clearAfter’, template: _.template($('#event-comment-template').html()), events: {'click a.removeComment': 'removeComment’}, render: function () { var template_vars = this.model.toJSON(); template_vars.text = replaceURLWithHTMLLinks(template_vars.comment); $(this.el).html(this.template(template_vars)); $(this.el).attr('data-id', this.model.get('commentId')); $('h3', $(this.el)).css('color', this.model.get('color')); return this; }, removeComment: function () { this.model.destroy(); this.remove(); } });
  • 21.
  • 22.
  • 23.
    Actual collection definition Weekendee.Collections.FriendRequests= Backbone.Collection.extend({ page: 1, url: function () { return base_url + 'user/list_friend_requests/?page=' + this.page; }, parse: function (resp) { this.page = resp.page; this.perPage = resp.per_page; this.total = resp.total; return resp.models; } });
  • 24.
    Actual collection creation Weekendee.Views.ContactsPage= Backbone.View.extend({ "el": "#contacts", show: function () { this.friendRequestsView = new Weekendee.Views.FriendRequests({ collection: new Weekendee.Collections.FriendRequests() }); …
  • 25.
    Actual collection view Weekendee.Views.FriendRequests= Backbone.View.extend({ 'el': '#friend_requests’, initialize: function () { this.collection.bind('reset', this.reset, this); this.collection.fetch(); }, reset: function (FriendRequestWrapper) { var me = this; $('.contacts-body', $(this.el)).html(''); $.each(FriendRequestWrapper.models, function (i, model) { var view = new Weekendee.Views.FriendRequest({ model: new Weekendee.Models.Contact(model) }); $('.contacts-body', $(me.el)).append(view.render().el); }); } });
  • 26.
  • 27.
  • 28.
    Actual router Weekendee.Routers.AppRouter =Backbone.Router.extend({ routes: { "/week": "week", // #/week "/": "week", "": "week", "/contacts": "contacts", // #/week //"/week/:year/:week": "week",// #/week/2011/13 //"/week/:startdate": "week", // #/week/2011-10-07 "/event/:id": "event", // #/event/123 "/event/:id/:uid": "event", // external facebook links "/vibe/:id": "vibe", // #/vibe/123 "/invite/:id": "invite", // #/vibe/123 "/*": "initial” }, week: function () { Weekendee.WeekPage.reload(); Weekendee.WeekPage.show(); //Weekendee.WeekPage.setSlider(sliderState); Weekendee.ContactsPage.hide(); },
  • 29.
  • 30.
    Problems  Event propagation- loss of bind context var me = this; var comment = new Weekendee.Models.EventComment(commentModel); me.collection.add(comment); comment.bind('destroy', me.removeComment, me);  Hard to debug if you’re being dumb – model is not printable userModel.set(‘name’,’Dr. Who?’);
  • 31.
    Problems  Lack ofdocumentation / best practices…
  • 32.
    Problems  (because of)big variety of connectivity combinations: X Models (update/get updated by) Y Views with Z Collections  And thus.. How to bind stuff correctly?  Manage collection of views?