SlideShare una empresa de Scribd logo
1 de 88
Descargar para leer sin conexión
MVC Frameworks
 in Javascript
   Hjörtur Hilmarsson
       @hjortureh
Agenda


• Why MVC in Javascript ?
• Backbone & Spine
• Backbone fundamentals
• Backbone Tips & Tricks
Why MVC ?
“The world web
  is changed”
Evolution of web apps
Help!
Contact Us
Markup

<form>

!   <!-- Name input -->
!   <input id="name" name="name" type="text" placeholder="What is your name?" required />

!   <!-- Email input -->
!   <input id="email" name="email" type="email" placeholder="What is your email?" required />

!   <!-- Message input -->
!   <textarea id="message" name="message" placeholder="Hello!" required ></textarea>

!   <!--Send button -->
!   <input id="submit" name="submit" type="submit" value="Send" />

!   <!-- Message label -->
!   <span id="message" ></span>

</form>
Javascript - Old style

$("form").submit(function( e ) {
!   !    !   !
!   e.preventDefault();

!     // get values
      var $form = $(this);
      var data = {
          name: $form.find("[name=name]").val(),
          email: $form.find("[name=email]").val(),
          message: $form.find("[name=message]").val()
      };

      // ajax request
      $.ajax({
          type: "post",
          url: "/enquiry",
          contentType: "application/json",
          dataType: "json",
          data: data,
          success: function() {
               $form.find("#message").text("Message posted").fadeIn();
          },
          error: function() {
               $form.find("#message").text("Sorry, there was an error").fadeIn();
          }
      });
});
Controller - MVC style

$("form").submit(function( e ) {
!   !    !   !
!   e.preventDefault();

!     // get values
!     var $form = $(this);
!     var data = {
!     !   name: $form.find("[name=name]").val(),
!     !   email: $form.find("[name=email]").val(),
!     !   message: $form.find("[name=message]").val()
!     };

!     // model
!     var enquiry = new Enquiry( data );
!
!     enquiry.save(
!     !   function() {
!     !   !    $form.find("#message").text("Message posted");
!     !   },
!     !   function() {
!     !   !    $form.find("#message").text("Sorry, there was an error");
!     !   }
!     );
});
Model - MVC style

// constructor
var Enquiry = function( data ) {
!   this.data = data;
};

// save method
Enquiry.prototype.save = function( success, error ) {

!    // ajax request
!    $.ajax({
!    !   type: "post",
!    !   url: "/enquiry",
!    !   contentType: "application/json",
!    !   dataType: "json",
!    !   data: this.data,
!    !   success: success,
!    !   error: error
!    });

};
Backbone.js controller view
var   ContactUs = Backbone.View.extend({
!
!     // local variables
!     el: $("form").get(0),
!     events: { "submit": "submit" }
!     model: new Enquiry,

!     // constructor
!     initialize: function() {
!     !   this.model.bind("create", create, this );!
!     !   this.model.bind("error", error, this );!
!     },

!     // submit event
!     submit: function( e ) {
!     !   e.preventDefault();
!     !
!     !   var data = {
!     !   !    name: this.$("[name=name]").val(),
!     !   !    email: this.$("[name=email]").val(),
!     !   !    message: this.$("[name=message]").val()
!     !   };

!     !    this.model.save();
!     },

!     // success callback
!     create: function() {
!     !   this.$("#message").text("Message posted");
!     },

!     // error callback
!     error: function() {
!     !   this.$("#message").text("Sorry, there was an error");
!     }

});
Backbone.js model

 var Enquiry = Backbone.Model.extend({});
MVC Benefits

Structure
Classes, inheritance, common patterns.

Modular
Communication via events, lousily coupled & testable components.

Common services
Back and forward history, clients-side url resources, utilities.

Persistence layers
RESTful sync, local storage, web sockets and more.

Community
Patterns,  mixins, conferences and more.
Challenges


• Going out of the box
• Nested models
• Complex ajax requests
• Understanding the limitations
• Its still hard
Challenges




TodoMVC - http://addyosmani.github.com/todomvc/
To mvc, or not to mvc ?

Use for one page apps

Use for complex client-side UIs & crud


Use not only for UI sugar

Use not for just rendering HTML

Use not for inflexible backends
Web Apps
Backbone & Spine
• Created 2010 by Jeremy Ashkenas
• File size 5.4k
• Depends on Underscore.js ( 4k )
• Very popular
http://blog.fogcreek.com/the-trello-tech-stack/
https://engineering.linkedin.com/mobile/linkedin-ipad-using-local-storage-snappy-mobile-apps
Spine


• Inspired by Backbone
• Written in CoffeeScript by Alex McCaw
• File size 7k
• Introduced async UI concept
Text



http://hjortureh.tumblr.com/post/22117245794/spine-js-vs-backbone-js
Fundamentals
Modules

• Events
• Models
• Collections
• Views
• Routes
• History
Events
Events



• Consists of on, off & trigger methods
• All Backbone modules can trigger events
• All Javascript object can be extended with
  the Backbone events module
Event example


Event triggered inside User class when name is changed


  this.trigger("change:name", "Mr Hilmarsson");




Bind to a name change event

  user.on("change:name", function( name ) {
  !   alert( "Name changed to " + name );
  });
Models
Models


• Wrapper for JSON & syncing via JSON
• RESTful by default. Overwrite sync
  method to change persistence logic.
• Communicates via events ( create, change,
  destroy, sync, error, add , remove )
• Can handle validation
Model


var Todo = Backbone.Model.extend({

      defaults: {
         done: false
      },

      toggle: function() {
         this.save({done: !this.get("done")});
      },

      clear: function() {
        this.destroy();
      }

});
TodoMVC - example




   http://addyosmani.github.com/todomvc/architecture-examples/backbone/index.html
Collections
Collections


• List of models
• Fires events for collection and the models
• Keeps models sorted
• Includes many utility methods
Collection

var TodoList = Backbone.Collection.extend({

      model: Todo,

      done: function() {
         return this.filter(function(todo){ return todo.get('done'); });
      },

      remaining: function() {
         return this.without.apply(this, this.done() );
      },

      comparator: function(todo) {
        return todo.get('order');
      }

});
Views
Views

• Bridge the gap between the HTML and
  models
• DOM element ( this.el ) represents the
  context
• Uses jQuery / Zepto / ender for DOM
  manipulation
• Listens for UI events & model events
• Use render method to create view
Organizing views




       1:1
       View    Model
Todo view

var TodoView = Backbone.View.extend({

    tagName:     "li",

    template: _.template($('#item-template').html()),

    events: {
       "click .check"                : "toggleDone"
    },

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

         this.model.bind('change', this.render );
    },

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

    toggleDone: function() {
      this.model.toggle();
    }

    ...
}
Template



<script type="text/template" id="item-template">

  <div class="todo <%= done ? 'done' : '' %>">
    <div class="display">
      <input class="check" type="checkbox" <%= done ? 'checked="checked"' : '' %> />
      <label class="todo-content"><%= content %></label>
      <span class="todo-destroy"></span>
    </div>
    <div class="edit">
      <input class="todo-input" type="text" value="<%= content %>" />
    </div>
  </div>

</script>
App view

var AppView = Backbone.View.extend({

!   el: $("#todoapp"),

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

!   !     Todos.on('add',     this.addOne);
!   !     Todos.on('reset',   this.addAll);

!   !     Todos.fetch();
!   },

!   addOne: function(todo) {
!   !   var view = new TodoView({model: todo});
!   !   this.$("#todo-list").append(view.render().el);
!   },

!   addAll: function() {
!   !   Todos.each(this.addOne);
!   }

!   ...

}
Router & History
Router & History


• Provides a way to map URL resources
• Enables client-side back & forward
  navigation
• Use Hash-change by default. Supports
  push state ( History API )  
Be Careful!



• Its stateful !
• Its not easy
• Don’t set navigate trigger to true
Router


APP.Router = Backbone.Router.extend({

  routes: {
     "new": "newNote",
     ":id": "editNote",
     "": "home"
  },

  home: function() {
     APP.appView.home();
  },

  newNote: function() {
     APP.appView.newNote();
  },

  editNote: function( id ) {
    APP.appView.editNote( id );
  }

});
History - example


Start listening for hash-change events

  // Start the history
  Backbone.history.start();




 Use html5 history API

  // Start the history
  Backbone.history.start({pushState: true});
Demo
Backbone tips & tricks
Tips & Tricks
•   Tip #1 - Bootstrapping data
•   Tip #2 - Async user interfaces
•   Tip #3 - Nested models
•   Tip #4 - Custom ajax requests
•   Tip #5 - Zombies to heaven
•   Tip #6 - The toolbox
•   Tip #7 - Test, test, test
•   Tip #8 - CoffeeScript
•   Tip #9 - Remember the basics
•   Tip #10 - Bonus points
Tip #1
Bootstrapping data
Bootstrapping data



• Using fetch extends waiting time
• Possible to bootstrap the most important
  data when the page is rendered
• No loading spinners !
Bootstrapping Data

The code

 // Current user
 APP.currentUser = new APP.Models.User(<%= @current_user.to_json.html_safe %>);

 // Notes
 APP.notes.reset(<%= @notes.to_json.html_safe %>);




After render
 // Current user
 APP.currentUser = new APP.Models.User({
   id: 1, username: "hjortureh",
   name: "Hjortur Hilmarsson",
   avatar: "avatar.gif"
 });

 // Notes
 APP.notes.reset([
   { id: 1, text: "Note 1" },
   { id: 1, text: "Note 2" },
   { id: 1, text: "Note 3" }
 ]);
Demo
Twitter demo
Tip #2
Async User Interfaces
Importance of speed
     Amazon 
     100 ms of extra load time caused a 1% drop in
     sales (source: Greg Linden, Amazon).


     Google
     500 ms of extra load time caused 20% fewer
     searches (source: Marrissa Mayer, Google).


     Yahoo! 
     400 ms of extra load time caused a 5–9%
     increase in the number of people who clicked
     “back” before the page even loaded (source:
     Nicole Sullivan, Yahoo!).


     37 Signals - Basecamp
     500 ms increase in speed on basecamp.com
     resulted in 5% improvement in conversion rate.
Importance of speed
Async user interfaces


• Models are optimistic by default
• UI is updated before server response
• Use cid as a unique identifier on the client
• No loading spinners !
Demo
Tip #3
Nested Models
Question

   Has many




Answers
Nested models


• Nested models are common
• No official way of doing it
• Overwrite parse after ajax request

• Overwrite toJSON before ajax request

• Backbone-relational mixin could help
Nested models
 var Question = Backbone.Model.extend({


   initialize: function() {

        // collection instance
        this.answers = new Answers;

   },


   parse: function(resp, xhr) {

        // fill nested model
        if( _.isArray( resp.answers ) ) {
            this.answers.reset( resp.answers );
        }

        return resp;

   },


   toJSON: function() {

        // send nested models
        return $.extend(
           this.attributes(), {
             answers: this.answers.toJSON()
           }
        );
   }

 });
Tip #4
Custom ajax requests
Custom ajax request



• Sometimes RESTful methods are not
  enough
• Example: Sorting tasks in to-do list
Sorting - Custom request



saveOrder: function() {
    !
!   var ids = this.pluck("id");
!
!   window.$.ajax({
!   !    url: "/tasks/reorder",
!   !    data: {
!   !    !   ids: ids
!   !    },
!   !    type: "POST",
!   !    dataType: "json",
!   !    complete: function() {
!   !    !   // Handle response
!   !    }
!   });
!
}
Tip #5
Send zombies to heaven
Zombies to heaven



• Its not enough to remove views from the
  DOM
• Events must be released so you don’t have
  zombies walking around
Zombies to heaven

// same as this.$el.remove();
this.remove();

// remove all models bindings
// made by this view
this.model.off( null, null, this );

// unbind events that are
// set on this view
this.off();
Tip #6
Use the toolbox
Use the toolbox


• Underscore has some wonderful methods
• isFunction, isObject, isString, isNumber,
  isDate & more.
• Underscore: http://
  documentcloud.github.com/underscore
Underscore
Line 865 from the Backbone.js code.


  // Underscore methods that we want to implement on the Collection.
  var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find',
      'detect', 'filter', 'select', 'reject', 'every', 'all', 'some', 'any',
      'include', 'contains', 'invoke', 'max', 'min', 'sortBy', 'sortedIndex',
      'toArray', 'size', 'first', 'initial', 'rest', 'last', 'without', 'indexOf',
      'shuffle', 'lastIndexOf', 'isEmpty', 'groupBy'];

  // Mix in each Underscore method as a proxy to `Collection#models`.
  _.each(methods, function(method) {
      Collection.prototype[method] = function() {
         return _[method].apply(_, [this.models].concat(_.toArray(arguments)));
      };
  });
Tip #7
Test, test, test
Testing


• Recommend Jasmine for testing

• Recommend Sinon to fake the server

• jQuery-jasmine to test views

• Use setDomLibrary method to fake jQuery
Jasmine with fake server & spy
 it('Should sync correctly', function () {

       // mockup data
       var note = new APP.Models.Note({ text: "Buy some eggs" });

       // fake server
       this.server = sinon.fakeServer.create();

       // fake response
       this.server.respondWith( "POST", "/notes",
          [ 200,
            {"Content-Type": "application/json"},
            '{ "id": 1, "text": "Remember the milk" }' ]
       );

       // spy on sync event
       var spy = sinon.spy();
       note.on("sync", spy );

       // save model
       note.save();

       // server repsonse
       this.server.respond();

       // assert
       expect( spy ).toHaveBeenCalledOnce();
       expect( spy ).toHaveBeenCalledWith( note );
       expect( note.get("text") ).toEqual( "Remember the milk" );

       // restore fake server
       this.server.restore();

 });
Demo
Tip #8
CoffeeScript
CoffeeScript


• Advanced programing language
• Compiles to javascript
• Same creator of Backbone and
  CoffeeScript
• Integrates well with Backbone
Coffee Script example
Extending Backbone module


  class TodoList extends Backbone.View




Double arrow to bind to the context
Use @ instead of this
Last line is the return value, returns this

 _.bindAll( this, 'render' )

 render: =>
   @$el.html( @template( @.model.toJSON() ))
   @




Need to call super on parent constructors

 initialize: ->
 !    super
Tip #9
The Basics
The basics


• The basics still apply with MVC in place
• Minimize ajax requests
• Keep your views thin & models fat
• Understanding Javascript is the key
Tip #10
 Bonus
Bonus points



• Read the documentation
• Read the source code
• Just do it !
Tack så mycket
  Hjörtur Elvar Hilmarsson
        @hjortureh

Más contenido relacionado

La actualidad más candente

The go-start webframework (GTUG Vienna 27.03.2012)
The go-start webframework (GTUG Vienna 27.03.2012)The go-start webframework (GTUG Vienna 27.03.2012)
The go-start webframework (GTUG Vienna 27.03.2012)
ungerik
 
Unobtrusive javascript with jQuery
Unobtrusive javascript with jQueryUnobtrusive javascript with jQuery
Unobtrusive javascript with jQuery
Angel Ruiz
 
Stack Overflow Austin - jQuery for Developers
Stack Overflow Austin - jQuery for DevelopersStack Overflow Austin - jQuery for Developers
Stack Overflow Austin - jQuery for Developers
Jonathan Sharp
 

La actualidad más candente (20)

jQuery: Events, Animation, Ajax
jQuery: Events, Animation, AjaxjQuery: Events, Animation, Ajax
jQuery: Events, Animation, Ajax
 
jQuery Loves Developers - Oredev 2009
jQuery Loves Developers - Oredev 2009jQuery Loves Developers - Oredev 2009
jQuery Loves Developers - Oredev 2009
 
jQuery for beginners
jQuery for beginnersjQuery for beginners
jQuery for beginners
 
Angular JS blog tutorial
Angular JS blog tutorialAngular JS blog tutorial
Angular JS blog tutorial
 
jQuery Presentation
jQuery PresentationjQuery Presentation
jQuery Presentation
 
Getting the Most Out of jQuery Widgets
Getting the Most Out of jQuery WidgetsGetting the Most Out of jQuery Widgets
Getting the Most Out of jQuery Widgets
 
jQuery in the [Aol.] Enterprise
jQuery in the [Aol.] EnterprisejQuery in the [Aol.] Enterprise
jQuery in the [Aol.] Enterprise
 
jQuery Essentials
jQuery EssentialsjQuery Essentials
jQuery Essentials
 
Sane Async Patterns
Sane Async PatternsSane Async Patterns
Sane Async Patterns
 
Jquery Complete Presentation along with Javascript Basics
Jquery Complete Presentation along with Javascript BasicsJquery Complete Presentation along with Javascript Basics
Jquery Complete Presentation along with Javascript Basics
 
AnkaraJUG Kasım 2012 - PrimeFaces
AnkaraJUG Kasım 2012 - PrimeFacesAnkaraJUG Kasım 2012 - PrimeFaces
AnkaraJUG Kasım 2012 - PrimeFaces
 
The go-start webframework (GTUG Vienna 27.03.2012)
The go-start webframework (GTUG Vienna 27.03.2012)The go-start webframework (GTUG Vienna 27.03.2012)
The go-start webframework (GTUG Vienna 27.03.2012)
 
Jquery In Rails
Jquery In RailsJquery In Rails
Jquery In Rails
 
AngularJS vs. Ember.js vs. Backbone.js
AngularJS vs. Ember.js vs. Backbone.jsAngularJS vs. Ember.js vs. Backbone.js
AngularJS vs. Ember.js vs. Backbone.js
 
BPM-2 Introduction to Advanced Workflows
BPM-2 Introduction to Advanced WorkflowsBPM-2 Introduction to Advanced Workflows
BPM-2 Introduction to Advanced Workflows
 
Unobtrusive javascript with jQuery
Unobtrusive javascript with jQueryUnobtrusive javascript with jQuery
Unobtrusive javascript with jQuery
 
Stack Overflow Austin - jQuery for Developers
Stack Overflow Austin - jQuery for DevelopersStack Overflow Austin - jQuery for Developers
Stack Overflow Austin - jQuery for Developers
 
SharePoint and jQuery Essentials
SharePoint and jQuery EssentialsSharePoint and jQuery Essentials
SharePoint and jQuery Essentials
 
BPM-1 Introduction to Advanced Workflows
BPM-1 Introduction to Advanced WorkflowsBPM-1 Introduction to Advanced Workflows
BPM-1 Introduction to Advanced Workflows
 
BPM-3 Advanced Workflow Deep Dive
BPM-3 Advanced Workflow Deep DiveBPM-3 Advanced Workflow Deep Dive
BPM-3 Advanced Workflow Deep Dive
 

Similar a Javascript MVC & Backbone Tips & Tricks

HTML5 - The 2012 of the Web
HTML5 - The 2012 of the WebHTML5 - The 2012 of the Web
HTML5 - The 2012 of the Web
Robert Nyman
 
Jarv.us Showcase — SenchaCon 2011
Jarv.us Showcase — SenchaCon 2011Jarv.us Showcase — SenchaCon 2011
Jarv.us Showcase — SenchaCon 2011
Chris Alfano
 
Week 4 - jQuery + Ajax
Week 4 - jQuery + AjaxWeek 4 - jQuery + Ajax
Week 4 - jQuery + Ajax
baygross
 
HTML5 - The 2012 of the Web - Adobe MAX
HTML5 - The 2012 of the Web - Adobe MAXHTML5 - The 2012 of the Web - Adobe MAX
HTML5 - The 2012 of the Web - Adobe MAX
Robert Nyman
 
Primefaces Nextgen Lju
Primefaces Nextgen LjuPrimefaces Nextgen Lju
Primefaces Nextgen Lju
Skills Matter
 

Similar a Javascript MVC & Backbone Tips & Tricks (20)

Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenery
 
FrontInBahia 2014: 10 dicas de desempenho para apps mobile híbridas
FrontInBahia 2014: 10 dicas de desempenho para apps mobile híbridasFrontInBahia 2014: 10 dicas de desempenho para apps mobile híbridas
FrontInBahia 2014: 10 dicas de desempenho para apps mobile híbridas
 
Backbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVCBackbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVC
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejs
 
Building a real life application in node js
Building a real life application in node jsBuilding a real life application in node js
Building a real life application in node js
 
Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node js
 
HTML5 - The 2012 of the Web
HTML5 - The 2012 of the WebHTML5 - The 2012 of the Web
HTML5 - The 2012 of the Web
 
Jarv.us Showcase — SenchaCon 2011
Jarv.us Showcase — SenchaCon 2011Jarv.us Showcase — SenchaCon 2011
Jarv.us Showcase — SenchaCon 2011
 
WordPress as the Backbone(.js)
WordPress as the Backbone(.js)WordPress as the Backbone(.js)
WordPress as the Backbone(.js)
 
Introduction to AngularJS
Introduction to AngularJSIntroduction to AngularJS
Introduction to AngularJS
 
[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC
 
Week 4 - jQuery + Ajax
Week 4 - jQuery + AjaxWeek 4 - jQuery + Ajax
Week 4 - jQuery + Ajax
 
Turn your spaghetti code into ravioli with JavaScript modules
Turn your spaghetti code into ravioli with JavaScript modulesTurn your spaghetti code into ravioli with JavaScript modules
Turn your spaghetti code into ravioli with JavaScript modules
 
Oracle Application Express & jQuery Mobile - OGh Apex Dag 2012
Oracle Application Express & jQuery Mobile - OGh Apex Dag 2012Oracle Application Express & jQuery Mobile - OGh Apex Dag 2012
Oracle Application Express & jQuery Mobile - OGh Apex Dag 2012
 
Aplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com BackboneAplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com Backbone
 
Rich Portlet Development in uPortal
Rich Portlet Development in uPortalRich Portlet Development in uPortal
Rich Portlet Development in uPortal
 
HTML5 - The 2012 of the Web - Adobe MAX
HTML5 - The 2012 of the Web - Adobe MAXHTML5 - The 2012 of the Web - Adobe MAX
HTML5 - The 2012 of the Web - Adobe MAX
 
Velocity EU 2014 — Offline-first web apps
Velocity EU 2014 — Offline-first web appsVelocity EU 2014 — Offline-first web apps
Velocity EU 2014 — Offline-first web apps
 
Primefaces Nextgen Lju
Primefaces Nextgen LjuPrimefaces Nextgen Lju
Primefaces Nextgen Lju
 
Primefaces Nextgen Lju
Primefaces Nextgen LjuPrimefaces Nextgen Lju
Primefaces Nextgen Lju
 

Último

+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 

Último (20)

Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 

Javascript MVC & Backbone Tips & Tricks

  • 1. MVC Frameworks in Javascript Hjörtur Hilmarsson @hjortureh
  • 2. Agenda • Why MVC in Javascript ? • Backbone & Spine • Backbone fundamentals • Backbone Tips & Tricks
  • 4. “The world web is changed”
  • 8. Markup <form> ! <!-- Name input --> ! <input id="name" name="name" type="text" placeholder="What is your name?" required /> ! <!-- Email input --> ! <input id="email" name="email" type="email" placeholder="What is your email?" required /> ! <!-- Message input --> ! <textarea id="message" name="message" placeholder="Hello!" required ></textarea> ! <!--Send button --> ! <input id="submit" name="submit" type="submit" value="Send" /> ! <!-- Message label --> ! <span id="message" ></span> </form>
  • 9. Javascript - Old style $("form").submit(function( e ) { ! ! ! ! ! e.preventDefault(); ! // get values var $form = $(this); var data = { name: $form.find("[name=name]").val(), email: $form.find("[name=email]").val(), message: $form.find("[name=message]").val() }; // ajax request $.ajax({ type: "post", url: "/enquiry", contentType: "application/json", dataType: "json", data: data, success: function() { $form.find("#message").text("Message posted").fadeIn(); }, error: function() { $form.find("#message").text("Sorry, there was an error").fadeIn(); } }); });
  • 10. Controller - MVC style $("form").submit(function( e ) { ! ! ! ! ! e.preventDefault(); ! // get values ! var $form = $(this); ! var data = { ! ! name: $form.find("[name=name]").val(), ! ! email: $form.find("[name=email]").val(), ! ! message: $form.find("[name=message]").val() ! }; ! // model ! var enquiry = new Enquiry( data ); ! ! enquiry.save( ! ! function() { ! ! ! $form.find("#message").text("Message posted"); ! ! }, ! ! function() { ! ! ! $form.find("#message").text("Sorry, there was an error"); ! ! } ! ); });
  • 11. Model - MVC style // constructor var Enquiry = function( data ) { ! this.data = data; }; // save method Enquiry.prototype.save = function( success, error ) { ! // ajax request ! $.ajax({ ! ! type: "post", ! ! url: "/enquiry", ! ! contentType: "application/json", ! ! dataType: "json", ! ! data: this.data, ! ! success: success, ! ! error: error ! }); };
  • 12. Backbone.js controller view var ContactUs = Backbone.View.extend({ ! ! // local variables ! el: $("form").get(0), ! events: { "submit": "submit" } ! model: new Enquiry, ! // constructor ! initialize: function() { ! ! this.model.bind("create", create, this );! ! ! this.model.bind("error", error, this );! ! }, ! // submit event ! submit: function( e ) { ! ! e.preventDefault(); ! ! ! ! var data = { ! ! ! name: this.$("[name=name]").val(), ! ! ! email: this.$("[name=email]").val(), ! ! ! message: this.$("[name=message]").val() ! ! }; ! ! this.model.save(); ! }, ! // success callback ! create: function() { ! ! this.$("#message").text("Message posted"); ! }, ! // error callback ! error: function() { ! ! this.$("#message").text("Sorry, there was an error"); ! } });
  • 13. Backbone.js model var Enquiry = Backbone.Model.extend({});
  • 14.
  • 15. MVC Benefits Structure Classes, inheritance, common patterns. Modular Communication via events, lousily coupled & testable components. Common services Back and forward history, clients-side url resources, utilities. Persistence layers RESTful sync, local storage, web sockets and more. Community Patterns,  mixins, conferences and more.
  • 16. Challenges • Going out of the box • Nested models • Complex ajax requests • Understanding the limitations • Its still hard
  • 18. To mvc, or not to mvc ? Use for one page apps Use for complex client-side UIs & crud Use not only for UI sugar Use not for just rendering HTML Use not for inflexible backends
  • 21. • Created 2010 by Jeremy Ashkenas • File size 5.4k • Depends on Underscore.js ( 4k ) • Very popular
  • 23.
  • 25. Spine • Inspired by Backbone • Written in CoffeeScript by Alex McCaw • File size 7k • Introduced async UI concept
  • 28. Modules • Events • Models • Collections • Views • Routes • History
  • 30. Events • Consists of on, off & trigger methods • All Backbone modules can trigger events • All Javascript object can be extended with the Backbone events module
  • 31. Event example Event triggered inside User class when name is changed this.trigger("change:name", "Mr Hilmarsson"); Bind to a name change event user.on("change:name", function( name ) { ! alert( "Name changed to " + name ); });
  • 33. Models • Wrapper for JSON & syncing via JSON • RESTful by default. Overwrite sync method to change persistence logic. • Communicates via events ( create, change, destroy, sync, error, add , remove ) • Can handle validation
  • 34. Model var Todo = Backbone.Model.extend({ defaults: { done: false }, toggle: function() { this.save({done: !this.get("done")}); }, clear: function() { this.destroy(); } });
  • 35. TodoMVC - example http://addyosmani.github.com/todomvc/architecture-examples/backbone/index.html
  • 37. Collections • List of models • Fires events for collection and the models • Keeps models sorted • Includes many utility methods
  • 38. Collection var TodoList = Backbone.Collection.extend({ model: Todo, done: function() { return this.filter(function(todo){ return todo.get('done'); }); }, remaining: function() { return this.without.apply(this, this.done() ); }, comparator: function(todo) { return todo.get('order'); } });
  • 39. Views
  • 40. Views • Bridge the gap between the HTML and models • DOM element ( this.el ) represents the context • Uses jQuery / Zepto / ender for DOM manipulation • Listens for UI events & model events • Use render method to create view
  • 41. Organizing views 1:1 View Model
  • 42. Todo view var TodoView = Backbone.View.extend({ tagName: "li", template: _.template($('#item-template').html()), events: { "click .check" : "toggleDone" }, initialize: function() { _.bindAll(this, 'render' ); this.model.bind('change', this.render ); }, render: function() { $(this.el).html(this.template(this.model.toJSON())); return this; }, toggleDone: function() { this.model.toggle(); } ... }
  • 43. Template <script type="text/template" id="item-template"> <div class="todo <%= done ? 'done' : '' %>"> <div class="display"> <input class="check" type="checkbox" <%= done ? 'checked="checked"' : '' %> /> <label class="todo-content"><%= content %></label> <span class="todo-destroy"></span> </div> <div class="edit"> <input class="todo-input" type="text" value="<%= content %>" /> </div> </div> </script>
  • 44. App view var AppView = Backbone.View.extend({ ! el: $("#todoapp"), ! ! initialize: function() { ! ! _.bindAll(this, 'addOne', 'addAll', 'render' ); ! ! Todos.on('add', this.addOne); ! ! Todos.on('reset', this.addAll); ! ! Todos.fetch(); ! }, ! addOne: function(todo) { ! ! var view = new TodoView({model: todo}); ! ! this.$("#todo-list").append(view.render().el); ! }, ! addAll: function() { ! ! Todos.each(this.addOne); ! } ! ... }
  • 46. Router & History • Provides a way to map URL resources • Enables client-side back & forward navigation • Use Hash-change by default. Supports push state ( History API )  
  • 47. Be Careful! • Its stateful ! • Its not easy • Don’t set navigate trigger to true
  • 48. Router APP.Router = Backbone.Router.extend({ routes: { "new": "newNote", ":id": "editNote", "": "home" }, home: function() { APP.appView.home(); }, newNote: function() { APP.appView.newNote(); }, editNote: function( id ) { APP.appView.editNote( id ); } });
  • 49. History - example Start listening for hash-change events // Start the history Backbone.history.start(); Use html5 history API // Start the history Backbone.history.start({pushState: true});
  • 50. Demo
  • 51. Backbone tips & tricks
  • 52. Tips & Tricks • Tip #1 - Bootstrapping data • Tip #2 - Async user interfaces • Tip #3 - Nested models • Tip #4 - Custom ajax requests • Tip #5 - Zombies to heaven • Tip #6 - The toolbox • Tip #7 - Test, test, test • Tip #8 - CoffeeScript • Tip #9 - Remember the basics • Tip #10 - Bonus points
  • 54. Bootstrapping data • Using fetch extends waiting time • Possible to bootstrap the most important data when the page is rendered • No loading spinners !
  • 55. Bootstrapping Data The code // Current user APP.currentUser = new APP.Models.User(<%= @current_user.to_json.html_safe %>); // Notes APP.notes.reset(<%= @notes.to_json.html_safe %>); After render // Current user APP.currentUser = new APP.Models.User({ id: 1, username: "hjortureh", name: "Hjortur Hilmarsson", avatar: "avatar.gif" }); // Notes APP.notes.reset([ { id: 1, text: "Note 1" }, { id: 1, text: "Note 2" }, { id: 1, text: "Note 3" } ]);
  • 56. Demo
  • 58. Tip #2 Async User Interfaces
  • 59. Importance of speed Amazon  100 ms of extra load time caused a 1% drop in sales (source: Greg Linden, Amazon). Google 500 ms of extra load time caused 20% fewer searches (source: Marrissa Mayer, Google). Yahoo!  400 ms of extra load time caused a 5–9% increase in the number of people who clicked “back” before the page even loaded (source: Nicole Sullivan, Yahoo!). 37 Signals - Basecamp 500 ms increase in speed on basecamp.com resulted in 5% improvement in conversion rate.
  • 61. Async user interfaces • Models are optimistic by default • UI is updated before server response • Use cid as a unique identifier on the client • No loading spinners !
  • 62. Demo
  • 64. Question Has many Answers
  • 65. Nested models • Nested models are common • No official way of doing it • Overwrite parse after ajax request • Overwrite toJSON before ajax request • Backbone-relational mixin could help
  • 66. Nested models var Question = Backbone.Model.extend({ initialize: function() { // collection instance this.answers = new Answers; }, parse: function(resp, xhr) { // fill nested model if( _.isArray( resp.answers ) ) { this.answers.reset( resp.answers ); } return resp; }, toJSON: function() { // send nested models return $.extend( this.attributes(), { answers: this.answers.toJSON() } ); } });
  • 67. Tip #4 Custom ajax requests
  • 68. Custom ajax request • Sometimes RESTful methods are not enough • Example: Sorting tasks in to-do list
  • 69.
  • 70. Sorting - Custom request saveOrder: function() { ! ! var ids = this.pluck("id"); ! ! window.$.ajax({ ! ! url: "/tasks/reorder", ! ! data: { ! ! ! ids: ids ! ! }, ! ! type: "POST", ! ! dataType: "json", ! ! complete: function() { ! ! ! // Handle response ! ! } ! }); ! }
  • 71. Tip #5 Send zombies to heaven
  • 72. Zombies to heaven • Its not enough to remove views from the DOM • Events must be released so you don’t have zombies walking around
  • 73. Zombies to heaven // same as this.$el.remove(); this.remove(); // remove all models bindings // made by this view this.model.off( null, null, this ); // unbind events that are // set on this view this.off();
  • 74. Tip #6 Use the toolbox
  • 75. Use the toolbox • Underscore has some wonderful methods • isFunction, isObject, isString, isNumber, isDate & more. • Underscore: http:// documentcloud.github.com/underscore
  • 76. Underscore Line 865 from the Backbone.js code. // Underscore methods that we want to implement on the Collection. var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find', 'detect', 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', 'max', 'min', 'sortBy', 'sortedIndex', 'toArray', 'size', 'first', 'initial', 'rest', 'last', 'without', 'indexOf', 'shuffle', 'lastIndexOf', 'isEmpty', 'groupBy']; // Mix in each Underscore method as a proxy to `Collection#models`. _.each(methods, function(method) { Collection.prototype[method] = function() { return _[method].apply(_, [this.models].concat(_.toArray(arguments))); }; });
  • 78. Testing • Recommend Jasmine for testing • Recommend Sinon to fake the server • jQuery-jasmine to test views • Use setDomLibrary method to fake jQuery
  • 79. Jasmine with fake server & spy it('Should sync correctly', function () { // mockup data var note = new APP.Models.Note({ text: "Buy some eggs" }); // fake server this.server = sinon.fakeServer.create(); // fake response this.server.respondWith( "POST", "/notes", [ 200, {"Content-Type": "application/json"}, '{ "id": 1, "text": "Remember the milk" }' ] ); // spy on sync event var spy = sinon.spy(); note.on("sync", spy ); // save model note.save(); // server repsonse this.server.respond(); // assert expect( spy ).toHaveBeenCalledOnce(); expect( spy ).toHaveBeenCalledWith( note ); expect( note.get("text") ).toEqual( "Remember the milk" ); // restore fake server this.server.restore(); });
  • 80. Demo
  • 82. CoffeeScript • Advanced programing language • Compiles to javascript • Same creator of Backbone and CoffeeScript • Integrates well with Backbone
  • 83. Coffee Script example Extending Backbone module class TodoList extends Backbone.View Double arrow to bind to the context Use @ instead of this Last line is the return value, returns this _.bindAll( this, 'render' ) render: => @$el.html( @template( @.model.toJSON() )) @ Need to call super on parent constructors initialize: -> ! super
  • 85. The basics • The basics still apply with MVC in place • Minimize ajax requests • Keep your views thin & models fat • Understanding Javascript is the key
  • 87. Bonus points • Read the documentation • Read the source code • Just do it !
  • 88. Tack så mycket Hjörtur Elvar Hilmarsson @hjortureh