Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Backbone.JS: Light-weight MVC for JavaScript

Leif Singer
November 24, 2011

Backbone.JS: Light-weight MVC for JavaScript

The slides for my talk at the November 2011 @HannoverJS meet-up.

"Writing Javascript-heavy client applications requires new ways of organizing your code. MVC has worked great in other domains — see how Backbone.JS translates this 32-year-old design pattern for today's web development practices."

Leif Singer

November 24, 2011
Tweet

More Decks by Leif Singer

Other Decks in Technology

Transcript

  1. Leif Singer | http://leif.me | @lsinger
    NOV 24th 2011
    LIGHT-WEIGHT MVC FOR JAVASCRIPT

    View Slide

  2. FLOWCHART
    2
    MVC
    ME
    BACKBONE.JS

    View Slide

  3. FLOWCHART
    3
    BACKBONE.JS
    APPLICATION
    STRUCTURE
    CONCEPTS
    EXAMPLE

    View Slide

  4. LEIF SINGER
    Software Engineering Group,
    Leibniz Universität Hannover
    –Teaching: Projects, Seminars
    –Research: Supporting SE
    using Social Software
    4

    View Slide

  5. LEIF SINGER
    Software Engineering Group,
    Leibniz Universität Hannover
    –Teaching: Projects, Seminars
    –Research: Supporting SE
    using Social Software
    4
    Web Development since 1996
    –private, start-up, employee, freelancer
    –HTML, JavaScript, Perl, CSS, PHP, Java, Groovy, ...
    –WebObjects, CakePHP, jQuery, Cappuccino, Grails, Play!, ...

    View Slide

  6. WHAT IS MVC?

    View Slide

  7. WHAT IS MVC?
    DESIGN
    PATTERN

    View Slide

  8. WHAT IS MVC?
    DESIGN
    PATTERN
    TRYGVE
    REENSKAUG

    View Slide

  9. WHAT IS MVC?
    DESIGN
    PATTERN
    TRYGVE
    REENSKAUG
    1979,
    XEROX PARC

    View Slide

  10. WHAT IS MVC?
    DESIGN
    PATTERN
    TRYGVE
    REENSKAUG
    1979,
    XEROX PARC
    DECOUPLES YOUR CODE INTO
    MODEL
    VIEW
    CONTROLLER

    View Slide

  11. 6
    MODEL
    MVC

    View Slide

  12. 6
    MODEL
    VIEW
    MVC

    View Slide

  13. 6
    MODEL
    VIEW
    READ
    MVC

    View Slide

  14. 6
    MODEL
    VIEW
    READ
    CHANGE
    EVENTS
    MVC

    View Slide

  15. 6
    MODEL
    VIEW
    READ
    USER EVENTS
    CHANGE
    EVENTS
    MVC

    View Slide

  16. 6
    MODEL
    VIEW CONTROLLER
    READ
    USER EVENTS
    CHANGE
    EVENTS
    MVC

    View Slide

  17. 6
    MODEL
    VIEW CONTROLLER
    READ CHANGE
    USER EVENTS
    CHANGE
    EVENTS
    MVC

    View Slide

  18. 6
    MODEL
    VIEW CONTROLLER
    READ CHANGE
    SELECT
    USER EVENTS
    CHANGE
    EVENTS
    MVC

    View Slide

  19. 6
    MODEL
    VIEW CONTROLLER
    READ CHANGE
    SELECT
    USER EVENTS
    CHANGE
    EVENTS
    MVC
    GREAT BENEFITS:
    • MODEL INDEPENDENT FROM
    CONTROLLER & VIEW
    • VIEW KNOWS ONLY THE MODEL
    IT DISPLAYS
    • CONTROLLER IS GLUE
    • ... STRUCTURE!

    View Slide

  20. ... AND ALONG CAME THE WEB.

    View Slide

  21. 8
    BROWSER WEB SERVER
    REQUEST GET /file.html
    RESPONSE
    200 OK

    View Slide

  22. 8
    BROWSER WEB SERVER
    REQUEST GET /file.html
    RESPONSE
    200 OK
    SERVER
    FORGETS
    CLIENT.

    View Slide

  23. 8
    BROWSER WEB SERVER
    REQUEST GET /file.html
    RESPONSE
    200 OK
    THE WEB’S ARCHITECTURE IS A SUCCESS!
    STATELESS: JUST ADD SERVERS
    PROXIES, CACHING
    UNIFORM INTERFACE
    EASY TO INTEGRATE NEW CLIENTS & SERVERS
    SAFE, IDEMPOTENT GET
    SERVER
    FORGETS
    CLIENT.

    View Slide

  24. 8
    BROWSER WEB SERVER
    REQUEST GET /file.html
    RESPONSE
    200 OK
    THE WEB’S ARCHITECTURE IS A SUCCESS!
    STATELESS: JUST ADD SERVERS
    PROXIES, CACHING
    UNIFORM INTERFACE
    EASY TO INTEGRATE NEW CLIENTS & SERVERS
    SAFE, IDEMPOTENT GET
    SERVER
    FORGETS
    CLIENT.
    BUT: OBSERVERS? USER EVENTS?
    CHANGE EVENTS?
    VIEW? MODEL!? CONTROLLER!?!
    AN ADAPTATION OF MVC IS NEEDED.

    View Slide

  25. 9
    MVC MODEL 2 / “WEB MVC”
    BROWSER
    WEB APPLICATION

    View Slide

  26. 9
    MVC MODEL 2 / “WEB MVC”
    BROWSER
    WEB APPLICATION
    FRONTCONTROLLER
    OR ROUTER

    View Slide

  27. 9
    MVC MODEL 2 / “WEB MVC”
    BROWSER
    WEB APPLICATION
    FRONTCONTROLLER
    OR ROUTER
    CONTROLLER

    View Slide

  28. 9
    MVC MODEL 2 / “WEB MVC”
    BROWSER
    WEB APPLICATION
    FRONTCONTROLLER
    OR ROUTER
    CONTROLLER MODEL STORAGE

    View Slide

  29. 9
    MVC MODEL 2 / “WEB MVC”
    BROWSER
    WEB APPLICATION
    FRONTCONTROLLER
    OR ROUTER
    CONTROLLER MODEL STORAGE

    View Slide

  30. 9
    MVC MODEL 2 / “WEB MVC”
    BROWSER
    WEB APPLICATION
    FRONTCONTROLLER
    OR ROUTER
    CONTROLLER
    VIEW
    MODEL STORAGE

    View Slide

  31. OMG NO ... AJAX!?!

    View Slide

  32. 11
    BROWSER WEB SERVER
    REQUEST GET /file.html
    RESPONSE
    200 OK
    SEQUENCE SHORTENED :)

    View Slide

  33. 11
    BROWSER WEB SERVER
    REQUEST GET /file.html
    RESPONSE
    200 OK
    JSON DATA
    P
    A
    G
    E
    FR
    A
    G
    M
    EN
    T
    SEQUENCE SHORTENED :)

    View Slide

  34. 11
    BROWSER WEB SERVER
    REQUEST GET /file.html
    RESPONSE
    200 OK
    JSON DATA
    P
    A
    G
    E
    FR
    A
    G
    M
    EN
    T
    SEQUENCE SHORTENED :)
    HORRIBLE
    MESS!

    View Slide

  35. 11
    BROWSER WEB SERVER
    REQUEST GET /file.html
    RESPONSE
    200 OK
    JSON DATA
    P
    A
    G
    E
    FR
    A
    G
    M
    EN
    T
    SEQUENCE SHORTENED :)
    HORRIBLE
    MESS!
    MVC FOR JAVASCRIPT
    APPLICATIONS?

    View Slide

  36. 11
    BROWSER WEB SERVER
    REQUEST GET /file.html
    RESPONSE
    200 OK
    JSON DATA
    P
    A
    G
    E
    FR
    A
    G
    M
    EN
    T
    SEQUENCE SHORTENED :)
    HORRIBLE
    MESS!
    MVC FOR JAVASCRIPT
    APPLICATIONS?
    YES!

    View Slide

  37. SHOW US BACKBONE ALREADY!

    View Slide

  38. 13
    BACKBONE.JS IS
    “MVC FOR JAVASCRIPT-HEAVY APPLICATIONS”
    CREATED BY DOCUMENTCLOUD.ORG
    HTTP://DOCUMENTCLOUD.GITHUB.COM/BACKBONE/

    View Slide

  39. 13
    LINKEDIN
    37SIGNALS
    FOURSQUARE
    SOUNDCLOUD
    GROUPON
    PANDORA
    FLOW
    AUDIOVROOM SALESFORCE DO
    TRAJECTORY
    CLOUDAPP
    BATTLEFIELD PLAY4FREE
    BLOSSOM.IO
    BITTORRENT
    BACKBONE.JS IS
    “MVC FOR JAVASCRIPT-HEAVY APPLICATIONS”
    CREATED BY DOCUMENTCLOUD.ORG
    HTTP://DOCUMENTCLOUD.GITHUB.COM/BACKBONE/

    View Slide

  40. 14
    WHAT DO WE NEED FOR MVC AGAIN?

    View Slide

  41. 15
    MODEL
    VIEW CONTROLLER
    READ CHANGE
    SELECT
    USER EVENTS
    CHANGE
    EVENTS

    View Slide

  42. 15
    MODEL
    VIEW CONTROLLER
    READ CHANGE
    SELECT
    USER EVENTS
    CHANGE
    EVENTS
    BACKBONE.MODEL BACKBONE.COLLECTION

    View Slide

  43. 15
    MODEL
    VIEW CONTROLLER
    READ CHANGE
    SELECT
    USER EVENTS
    CHANGE
    EVENTS
    BACKBONE.MODEL BACKBONE.COLLECTION
    TEMPLATES
    HTML

    View Slide

  44. 15
    MODEL
    VIEW CONTROLLER
    READ CHANGE
    SELECT
    USER EVENTS
    CHANGE
    EVENTS
    BACKBONE.MODEL BACKBONE.COLLECTION
    BACKBONE.VIEW
    BACKBONE.ROUTER
    TEMPLATES
    HTML

    View Slide

  45. 15
    MODEL
    VIEW CONTROLLER
    READ CHANGE
    SELECT
    USER EVENTS
    CHANGE
    EVENTS
    BACKBONE.MODEL BACKBONE.COLLECTION
    BACKBONE.EVENTS
    BACKBONE.EVENTS
    BACKBONE.VIEW
    BACKBONE.ROUTER
    TEMPLATES
    HTML

    View Slide

  46. 15
    MODEL
    VIEW CONTROLLER
    READ CHANGE
    SELECT
    USER EVENTS
    CHANGE
    EVENTS
    BACKBONE.MODEL BACKBONE.COLLECTION
    BACKBONE.EVENTS
    BACKBONE.EVENTS
    STORAGE BACKBONE.SYNC
    BACKBONE.VIEW
    BACKBONE.ROUTER
    TEMPLATES
    HTML

    View Slide

  47. 16
    OUR DOMAIN OBJECTS
    THE “THINGS” OUR APPLICATION IS ABOUT
    BACKBONE.MODEL
    MyApp.User = Backbone.Model.extend({});

    View Slide

  48. 17
    OUR DOMAIN OBJECTS
    THE “THINGS” OUR APPLICATION IS ABOUT
    BACKBONE.MODEL
    MyApp.User = Backbone.Model.extend({
    defaults: function() {
    return {
    username: "anonymous",
    thumbnail: "default.png"
    };
    }
    });

    View Slide

  49. 18
    OUR DOMAIN OBJECTS
    THE “THINGS” OUR APPLICATION IS ABOUT
    BACKBONE.MODEL
    MyApp.User = Backbone.Model.extend({
    defaults: function() {
    return {
    username: "anonymous",
    thumbnail: "default.png"
    };
    },
    hasUsername: function() {
    return this.get("username") !== "anonymous";
    }
    });

    View Slide

  50. 19
    COLLECTIONS OF OUR DOMAIN OBJECTS
    SORT THEM, FILTER THEM, ADD, REMOVE, SYNC, ...
    BACKBONE.COLLECTION
    MyApp.UserList = Backbone.Collection.extend({});
    MyApp.Users = new MyApp.UserList;

    View Slide

  51. 20
    COLLECTIONS OF OUR DOMAIN OBJECTS
    SORT THEM, FILTER THEM, ADD, REMOVE, SYNC, ...
    BACKBONE.COLLECTION
    MyApp.UserList = Backbone.Collection.extend({
    model: MyApp.User,
    url: "/users"
    });
    MyApp.Users = new MyApp.UserList;

    View Slide

  52. 21
    COLLECTIONS OF OUR DOMAIN OBJECTS
    SORT THEM, FILTER THEM, ADD, REMOVE, SYNC, ...
    BACKBONE.COLLECTION
    MyApp.UserList = Backbone.Collection.extend({
    model: MyApp.User,
    url: "/users",
    withUsername: function() {
    return this.filter(function(user) {
    return user.hasUsername();
    });
    },
    comparator: function(user) {
    return user.get("username");
    }
    });
    MyApp.Users = new MyApp.UserList;

    View Slide

  53. 22
    COLLECTIONS OF OUR DOMAIN OBJECTS
    NEAT FUNCTIONS FROM UNDERSCORE.JS!
    BACKBONE.COLLECTION
    // each: calls function for each element
    collection.each(function(element) {element.save();});

    View Slide

  54. 22
    COLLECTIONS OF OUR DOMAIN OBJECTS
    NEAT FUNCTIONS FROM UNDERSCORE.JS!
    BACKBONE.COLLECTION
    // each: calls function for each element
    collection.each(function(element) {element.save();});
    // filter: all those elements passing the test
    var goodOnes = collection.filter(function(element) {element.isGood});

    View Slide

  55. 22
    COLLECTIONS OF OUR DOMAIN OBJECTS
    NEAT FUNCTIONS FROM UNDERSCORE.JS!
    BACKBONE.COLLECTION
    // each: calls function for each element
    collection.each(function(element) {element.save();});
    // filter: all those elements passing the test
    var goodOnes = collection.filter(function(element) {element.isGood});
    // reject: all those elements *failing* the test
    var badOnes = collection.reject(function(element) {element.isGood});

    View Slide

  56. 22
    COLLECTIONS OF OUR DOMAIN OBJECTS
    NEAT FUNCTIONS FROM UNDERSCORE.JS!
    BACKBONE.COLLECTION
    // each: calls function for each element
    collection.each(function(element) {element.save();});
    // filter: all those elements passing the test
    var goodOnes = collection.filter(function(element) {element.isGood});
    // reject: all those elements *failing* the test
    var badOnes = collection.reject(function(element) {element.isGood});
    // find: returns first match
    var mm = collection.find(function(element) {element.username === "mmuster"});

    View Slide

  57. 23
    STORAGE BACKBONE.SYNC
    MANAGES PERSISTENCE FOR OUR MODELS
    DEFAULT: RESTFUL* HTTP INTERFACE
    REPLACEABLE! E.G. WITH LOCALSTORAGE
    * WELL, KINDA, SORTA. REST IS MORE THAN CRUD.

    View Slide

  58. 23
    STORAGE BACKBONE.SYNC
    MANAGES PERSISTENCE FOR OUR MODELS
    DEFAULT: RESTFUL* HTTP INTERFACE
    REPLACEABLE! E.G. WITH LOCALSTORAGE
    * WELL, KINDA, SORTA. REST IS MORE THAN CRUD.
    var user = new MyApp.User({
    username: "lsinger"
    });
    // "create" event => POST /users
    user.save();
    // "update" event => PUT /users/17
    user.save({thumbnail: "lsinger.png"});
    // "destroy" event => DELETE /users/17
    user.destroy();

    View Slide

  59. 24
    STORAGE BACKBONE.SYNC
    USER VIEW CONTROLLER MODEL SERVER
    BUY!
    SUBMIT()
    SAVE()
    POST /ORDERS

    View Slide

  60. 24
    STORAGE BACKBONE.SYNC
    USER VIEW CONTROLLER MODEL SERVER
    BUY!
    SUBMIT()
    SAVE()
    POST /ORDERS
    200 OK
    TRUE
    SUCCESS.SHOW()
    SUCCESS!

    View Slide

  61. 25
    STATIC APPLICATION SKELETON
    INSERTION POINTS FOR TEMPLATES
    HTML

    Profile




    View Slide

  62. 26
    DISPLAYS MODEL OBJECTS
    DEFAULT: UNDERSCORE’S TEMPLATE ENGINE
    REPLACEABLE WITH MUSTACHE, HAML-JS, ...
    TEMPLATES
    <br/><div class="user"><br/><div class="thumbnail"><br/><img src="<%= thumbnail %>" title="<%= username %>"/><br/></div><br/><span class="username"><br/><% if (username) { %><br/><%= username %><br/><% } else { %><br/>anonymous<br/><% } %><br/></span><br/></div><br/>

    View Slide

  63. 27
    ACTUALLY, IT’S THE CONTROLLER
    GLUES TOGETHER VIEWS AND MODELS VIA EVENTS
    INTERPRETS VIEW EVENTS (SUBMIT MEANS BUY)
    BACKBONE.VIEW
    MyApp.UserView = Backbone.View.extend({});

    View Slide

  64. 28
    BACKBONE.VIEW
    MyApp.UserView = Backbone.View.extend({
    el: $("#user-details"),
    template: _.template($("#user-template").html())
    });
    ACTUALLY, IT’S THE CONTROLLER
    GLUES TOGETHER VIEWS AND MODELS VIA EVENTS
    INTERPRETS VIEW EVENTS (SUBMIT MEANS BUY)

    View Slide

  65. 29
    BACKBONE.VIEW
    MyApp.UserView = Backbone.View.extend({
    el: $("#user-details"),
    template: _.template($("#user-template").html()),
    events: {
    "click div.thumbnail" : "zoomImage"
    },
    zoomImage: function(e) {
    // e: event object
    /* ... zoom image here ... */
    }
    });
    ACTUALLY, IT’S THE CONTROLLER
    GLUES TOGETHER VIEWS AND MODELS VIA EVENTS
    INTERPRETS VIEW EVENTS (SUBMIT MEANS BUY)

    View Slide

  66. 30
    BACKBONE.VIEW
    MyApp.UserView = Backbone.View.extend({
    el: $("#user-details"),
    template: _.template($("#user-template").html()),
    events: {
    "click div.thumbnail" : "zoomImage"
    },
    zoomImage: function(e) {
    // e: event object
    /* ... zoom image here ... */
    },
    initialize: function() {
    this.model.bind('change', this.render, this);
    }
    });
    ACTUALLY, IT’S THE CONTROLLER
    GLUES TOGETHER VIEWS AND MODELS VIA EVENTS
    INTERPRETS VIEW EVENTS (SUBMIT MEANS BUY)

    View Slide

  67. 31
    BACKBONE.VIEW
    MyApp.UserView = Backbone.View.extend({
    el: $("#user-details"),
    template: _.template($("#user-template").html()),
    events: {
    "click div.thumbnail" : "zoomImage"
    },
    zoomImage: function(e) {
    // e: event object
    /* ... zoom image here ... */
    },
    initialize: function() {
    this.model.bind('change', this.render, this);
    },
    render: function() {
    $(this.el).html(this.template(this.model.toJSON()));
    return this; // allow chaining
    }
    });
    ACTUALLY, IT’S THE CONTROLLER
    GLUES TOGETHER VIEWS AND MODELS VIA EVENTS
    INTERPRETS VIEW EVENTS (SUBMIT MEANS BUY)

    View Slide

  68. 32
    A FRONTCONTROLLER
    MAPS URLS TO APPLICATION STATE
    STATE BECOMES BOOKMARKABLE!
    HISTORY API WITH HASHBANG FALLBACK
    BACKBONE.ROUTER
    MyApp.Router = Backbone.Router.extend({
    routes: {
    "user/:username": "profile",
    "user/:username/posts": "posts"
    }
    });

    View Slide

  69. 33
    A FRONTCONTROLLER
    MAPS URLS TO APPLICATION STATE
    STATE BECOMES BOOKMARKABLE!
    HISTORY API WITH HASHBANG FALLBACK
    BACKBONE.ROUTER
    MyApp.Router = Backbone.Router.extend({
    routes: {
    "user/:username": "profile",
    "user/:username/posts": "posts"
    }
    });
    MyApp.Router.bind("route:profile", function(username) {
    // recreate application state here
    });
    MyApp.Router.bind("route:posts", function(username) {
    // recreate application state here
    });

    View Slide

  70. 34
    USED THROUGHOUT BACKBONE TO CONNECT THINGS
    BACKBONE.EVENTS
    // observable emits "something:happened" events
    var observable = {};
    _.extend(observable, Backbone.Events);
    observable.change = function() {
    observable.trigger("something:happened", "I changed!");
    };

    View Slide

  71. 34
    USED THROUGHOUT BACKBONE TO CONNECT THINGS
    BACKBONE.EVENTS
    // observable emits "something:happened" events
    var observable = {};
    _.extend(observable, Backbone.Events);
    observable.change = function() {
    observable.trigger("something:happened", "I changed!");
    };
    // somewhere else, a listener wants to listen
    var listener = function(msg) {console.log("Something happened: " + msg);};
    observable.bind("something:happened", listener);

    View Slide

  72. 34
    USED THROUGHOUT BACKBONE TO CONNECT THINGS
    BACKBONE.EVENTS
    // observable emits "something:happened" events
    var observable = {};
    _.extend(observable, Backbone.Events);
    observable.change = function() {
    observable.trigger("something:happened", "I changed!");
    };
    // somewhere else, a listener wants to listen
    var listener = function(msg) {console.log("Something happened: " + msg);};
    observable.bind("something:happened", listener);
    // somewhere else still, something actually happens
    observable.change();

    View Slide

  73. 34
    USED THROUGHOUT BACKBONE TO CONNECT THINGS
    BACKBONE.EVENTS
    // observable emits "something:happened" events
    var observable = {};
    _.extend(observable, Backbone.Events);
    observable.change = function() {
    observable.trigger("something:happened", "I changed!");
    };
    // somewhere else, a listener wants to listen
    var listener = function(msg) {console.log("Something happened: " + msg);};
    observable.bind("something:happened", listener);
    // somewhere else still, something actually happens
    observable.change();

    View Slide

  74. 34
    USED THROUGHOUT BACKBONE TO CONNECT THINGS
    BACKBONE.EVENTS
    // observable emits "something:happened" events
    var observable = {};
    _.extend(observable, Backbone.Events);
    observable.change = function() {
    observable.trigger("something:happened", "I changed!");
    };
    // somewhere else, a listener wants to listen
    var listener = function(msg) {console.log("Something happened: " + msg);};
    observable.bind("something:happened", listener);
    // somewhere else still, something actually happens
    observable.change();

    View Slide

  75. 35
    // use of events in a Backbone.View's initialize:
    initialize: function() {
    this.model.bind('change', this.render, this);
    this.model.bind('destroy', this.remove, this);
    }
    BACKBONE.EVENTS
    USED THROUGHOUT BACKBONE TO CONNECT THINGS

    View Slide

  76. 36

    View Slide

  77. 37

    View Slide

  78. 38

    View Slide

  79. 39

    View Slide

  80. 40

    View Slide

  81. 41

    View Slide

  82. WHAT IS A TODO?
    42
    TodoApp.Todo = Backbone.Model.extend({
    defaults: function() {
    return {
    done: false,
    text: "",
    order: TodoApp.Todos.nextOrder()
    };
    },
    toggle: function() {
    this.save({done: !this.get("done")});
    }
    });

    View Slide

  83. WHAT ABOUT MANY TODOS?
    43
    TodoApp.TodoList = Backbone.Collection.extend({
    model: TodoApp.Todo,
    localStorage: new Store("todos"),
    done: function() {
    return this.filter(function(todo){ return todo.get('done'); });
    },
    remaining: function() {
    return this.without.apply(this, this.done());
    },
    nextOrder: function() {
    if (!this.length) return 1;
    return this.last().get('order') + 1;
    },
    comparator: function(todo) {
    return todo.get('order');
    }
    });
    TodoApp.Todos = new TodoApp.TodoList;

    View Slide

  84. WHAT SHOULD A TODO LOOK LIKE?
    44
    <br/><div class="todo <%= done ? 'done' : '' %>"><br/><div class="display"><br/><input class="check" type="checkbox" <%= done ? 'checked="checked"' : '' %> /><br/><div class="todo-text"></div><br/><span class="todo-destroy"></span><br/></div><br/><div class="edit"><br/><input class="todo-input" type="text" value="" /><br/></div><br/></div><br/>

    View Slide

  85. DISPLAY STATISTICS & CLEAR BUTTON
    45
    <br/><% if (total) { %><br/><span class="todo-count"><br/><span class="number"><%= remaining %></span><br/><span class="word"><%= remaining == 1 ? 'item' : 'items' %></span> left.<br/></span><br/><% } %><br/><% if (done) { %><br/><span class="todo-clear"><br/><a href="#"><br/>Clear <span class="number-done"><%= done %></span><br/>completed <span class="word-done"><%= done == 1 ? 'item' : 'items' %></span><br/></a><br/></span><br/><% } %><br/>

    View Slide

  86. PUT IT ALL INTO A STATIC SKELETON
    46


    Todos











    View Slide

  87. 47

    View Slide

  88. 47
    NO BEHAVIOR YET.

    View Slide

  89. THE TODO CONTROLLER
    48
    TodoApp.TodoView = Backbone.View.extend({
    tagName: "li",
    template: _.template($('#item-template').html()),
    events: {
    "click .check" : "toggleDone",
    "dblclick div.todo-text" : "edit",
    "click span.todo-destroy" : "clear",
    "keypress .todo-input" : "updateOnEnter"
    },
    initialize: function() {
    this.model.bind('change', this.render, this);
    this.model.bind('destroy', this.remove, this);
    },
    render: function() {
    $(this.el).html(this.template(this.model.toJSON()));
    this.setText();
    return this;
    },
    setText: function() {
    var text = this.model.get('text');
    this.$('.todo-text').text(text);
    this.input = this.$('.todo-input');
    this.input.bind('blur', _.bind(this.close, this)).val(text);
    },
    /* continued ... */

    View Slide

  90. EVENT CALLBACKS FOR THE TODO CONTROLLER
    49
    toggleDone: function() {
    this.model.toggle();
    },
    edit: function() {
    $(this.el).addClass("editing");
    this.input.focus();
    },
    close: function() {
    this.model.save({text: this.input.val()});
    $(this.el).removeClass("editing");
    },
    updateOnEnter: function(e) {
    if (e.keyCode == 13) this.close();
    },
    remove: function() {
    $(this.el).remove();
    },
    clear: function() {
    this.model.destroy();
    }

    View Slide

  91. THE APPLICATION CONTROLLER
    50
    TodoApp.AppView = Backbone.View.extend({
    el: $("#todoapp"),
    statsTemplate: _.template($('#stats-template').html()),
    events: {
    "keypress #new-todo": "createOnEnter",
    "click .todo-clear a": "clearCompleted"
    },
    initialize: function() {
    this.input = this.$("#new-todo");
    TodoApp.Todos.bind('add', this.addOne, this);
    TodoApp.Todos.bind('reset', this.addAll, this);
    TodoApp.Todos.bind('all', this.render, this);
    TodoApp.Todos.fetch();
    },
    render: function() {
    this.$('#todo-stats').html(this.statsTemplate({
    total: TodoApp.Todos.length,
    done: TodoApp.Todos.done().length,
    remaining: TodoApp.Todos.remaining().length
    }));
    },
    /* continued ... */

    View Slide

  92. EVENT CALLBACKS FOR THE APPLICATION CONTROLLER
    51
    addOne: function(todo) {
    var view = new TodoApp.TodoView({model: todo});
    this.$("#todo-list").append(view.render().el);
    },
    addAll: function() {
    // first, clear the existing views
    this.$("#todo-list").empty();
    TodoApp.Todos.each(this.addOne);
    },
    createOnEnter: function(e) {
    var text = this.input.val();
    if (!text || e.keyCode != 13) return;
    TodoApp.Todos.create({text: text});
    this.input.val('');
    },
    clearCompleted: function() {
    _.each(TodoApp.Todos.done(), function(todo){ todo.destroy(); });
    return false;
    }

    View Slide

  93. 52
    HTTP://LOCALHOST:9000/TODO
    DEMO.

    View Slide

  94. 53
    FINALLY!
    WE NOW HAVE ALL THE MVC
    FRAMEWORKS WE NEED ...

    View Slide

  95. REALTIME!?!?

    View Slide

  96. REALTIME & BACKBONE.JS
    55
    MADE FOR “REST” INTERFACES
    COLLECTIONS ALWAYS FETCH ALL ITEMS
    CAN BE MODIFIED FOR USE WITH
    SOCKET.IO + NODE.JS
    JUGGERNAUT + NODE.JS (SPINE.JS!)
    ... NO TECH-NEUTRAL SOLUTION THERE YET.

    View Slide

  97. REALTIME & BACKBONE.JS
    55
    MADE FOR “REST” INTERFACES
    COLLECTIONS ALWAYS FETCH ALL ITEMS
    CAN BE MODIFIED FOR USE WITH
    SOCKET.IO + NODE.JS
    JUGGERNAUT + NODE.JS (SPINE.JS!)
    ... NO TECH-NEUTRAL SOLUTION THERE YET.
    CONVENTIONS & PATTERNS STILL IN FLUX

    View Slide

  98. 56
    HTTP://TODOS.HANNOVERJS.DE/
    BUT: A DIRTY LITTLE HACK ...

    View Slide

  99. 56
    HTTP://TODOS.HANNOVERJS.DE/
    BUT: A DIRTY LITTLE HACK ...
    TOO MUCH TRAFFIC
    EDITING IMPOSSIBLE
    VIEW GETS REPLACED BY UPDATES
    NO CONFLICT HANDLING

    View Slide

  100. 57
    SO, WHAT ABOUT MVC & REALTIME?

    View Slide

  101. 58
    THE SPECTRUM THAT IS THE WEB
    * INDEED, SEVERAL OF THE FRAMEWORKS LISTED CAN BE USED FOR REALTIME, E.G. SPINE + JUGGERNAUT. HOWEVER, THESE
    SOLUTIONS TYPICALLY DEPEND ON CERTAIN SERVER-TECHNOLOGIES, SUCH AS NODE.JS. I DON’T SEE A TECH-NEUTRAL
    PATTERN YET – OR RATHER, I SEE MANY COMPETING ONES. TIME WILL TELL WHICH ONE(S) WILL PREVAIL.

    View Slide

  102. 58
    THE SPECTRUM THAT IS THE WEB
    STATIC PAGES
    ENRICHED
    SOME AJAX DESKTOP-LIKE
    REALTIME*
    JS APPS
    ?
    * INDEED, SEVERAL OF THE FRAMEWORKS LISTED CAN BE USED FOR REALTIME, E.G. SPINE + JUGGERNAUT. HOWEVER, THESE
    SOLUTIONS TYPICALLY DEPEND ON CERTAIN SERVER-TECHNOLOGIES, SUCH AS NODE.JS. I DON’T SEE A TECH-NEUTRAL
    PATTERN YET – OR RATHER, I SEE MANY COMPETING ONES. TIME WILL TELL WHICH ONE(S) WILL PREVAIL.

    View Slide

  103. 58
    THE SPECTRUM THAT IS THE WEB
    STATIC PAGES
    ENRICHED
    SOME AJAX DESKTOP-LIKE
    REALTIME*
    BACKBONE.JS
    ANGULAR
    SPINE
    ...
    CAPPUCCINO
    SPROUTCORE
    ...
    JS APPS
    ?
    JQUERY UI
    ...
    ?
    JQUERY
    PROTOTYPE
    ...
    * INDEED, SEVERAL OF THE FRAMEWORKS LISTED CAN BE USED FOR REALTIME, E.G. SPINE + JUGGERNAUT. HOWEVER, THESE
    SOLUTIONS TYPICALLY DEPEND ON CERTAIN SERVER-TECHNOLOGIES, SUCH AS NODE.JS. I DON’T SEE A TECH-NEUTRAL
    PATTERN YET – OR RATHER, I SEE MANY COMPETING ONES. TIME WILL TELL WHICH ONE(S) WILL PREVAIL.

    View Slide

  104. 59

    View Slide

  105. 60

    View Slide

  106. 61

    View Slide

  107. ADVERTISEMENT!
    62

    View Slide

  108. ADVERTISEMENT!
    62
    LOOKING FOR
    COMPANIES WITH
    SOFTWARE
    DEVELOPERS!
    TAKE PART IN RESEARCH!
    HOW TO CREATE SOFTWARE
    FASTER & BETTER?
    TALK TO ME || HTTP://LEIF.ME

    View Slide

  109. CREDIT WHERE CREDIT IS DUE
    63
    • Backbone.JS logo: http://documentcloud.github.com/backbone/
    • MVC gang: http://youtube.com/watch?v=91C7ax0UAAc
    • spider web: http://www.flickr.com/photos/zzathras777/1546040168/
    • server: http://www.iconshock.com/
    • HTML, JS, CSS icons: http://www.drewwilson.com/
    • The Absolute Silence: http://www.flickr.com/photos/fabbriciuse/438451967/
    • Facepalm: http://www.flickr.com/photos/santiagogprg/5023648200/
    • Colorful World: http://mediablade.deviantart.com/art/Colorful-World-01-89864194
    • Todo List Application by Jérôme Gravel-Niquet: http://documentcloud.github.com/backbone/
    examples/todos/index.html

    View Slide