Pro Yearly is on sale from $80 to $50! »

Backbone.JS: Light-weight MVC for JavaScript

034b20e4eb21f55d3b96d3f2d923dd76?s=47 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."

034b20e4eb21f55d3b96d3f2d923dd76?s=128

Leif Singer

November 24, 2011
Tweet

Transcript

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

    MVC FOR JAVASCRIPT
  2. FLOWCHART 2 MVC ME BACKBONE.JS

  3. FLOWCHART 3 BACKBONE.JS APPLICATION STRUCTURE CONCEPTS EXAMPLE

  4. LEIF SINGER Software Engineering Group, Leibniz Universität Hannover –Teaching: Projects,

    Seminars –Research: Supporting SE using Social Software 4
  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!, ...
  6. WHAT IS MVC?

  7. WHAT IS MVC? DESIGN PATTERN

  8. WHAT IS MVC? DESIGN PATTERN TRYGVE REENSKAUG

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

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

    DECOUPLES YOUR CODE INTO MODEL VIEW CONTROLLER
  11. 6 MODEL MVC

  12. 6 MODEL VIEW MVC

  13. 6 MODEL VIEW READ MVC

  14. 6 MODEL VIEW READ CHANGE EVENTS MVC

  15. 6 MODEL VIEW READ USER EVENTS CHANGE EVENTS MVC

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

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

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

    EVENTS MVC
  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!
  20. ... AND ALONG CAME THE WEB.

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

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

    SERVER FORGETS CLIENT.
  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.
  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.
  25. 9 MVC MODEL 2 / “WEB MVC” BROWSER WEB APPLICATION

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

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

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

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

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

    FRONTCONTROLLER OR ROUTER CONTROLLER VIEW MODEL STORAGE
  31. OMG NO ... AJAX!?!

  32. 11 BROWSER WEB SERVER REQUEST GET /file.html RESPONSE 200 OK

    SEQUENCE SHORTENED :)
  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 :)
  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!
  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?
  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!
  37. SHOW US BACKBONE ALREADY!

  38. 13 BACKBONE.JS IS “MVC FOR JAVASCRIPT-HEAVY APPLICATIONS” CREATED BY DOCUMENTCLOUD.ORG

    HTTP://DOCUMENTCLOUD.GITHUB.COM/BACKBONE/
  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/
  40. 14 WHAT DO WE NEED FOR MVC AGAIN?

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

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

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

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

    EVENTS BACKBONE.MODEL BACKBONE.COLLECTION BACKBONE.VIEW BACKBONE.ROUTER TEMPLATES HTML
  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
  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
  47. 16 OUR DOMAIN OBJECTS THE “THINGS” OUR APPLICATION IS ABOUT

    BACKBONE.MODEL MyApp.User = Backbone.Model.extend({});
  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" }; } });
  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"; } });
  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;
  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;
  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;
  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();});
  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});
  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});
  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"});
  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.
  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();
  59. 24 STORAGE BACKBONE.SYNC USER VIEW CONTROLLER MODEL SERVER BUY! SUBMIT()

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

    SAVE() POST /ORDERS 200 OK TRUE SUCCESS.SHOW() SUCCESS!
  61. 25 STATIC APPLICATION SKELETON INSERTION POINTS FOR TEMPLATES HTML <div

    id="user-profile"> <h1>Profile</h1> <div id="user-details"> <!-- we'll insert details about the user here --> </div> </div>
  62. 26 DISPLAYS MODEL OBJECTS DEFAULT: UNDERSCORE’S TEMPLATE ENGINE REPLACEABLE WITH

    MUSTACHE, HAML-JS, ... TEMPLATES <script type="text/template" id="user-template"> <div class="user"> <div class="thumbnail"> <img src="<%= thumbnail %>" title="<%= username %>"/> </div> <span class="username"> <% if (username) { %> <%= username %> <% } else { %> anonymous <% } %> </span> </div> </script>
  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({});
  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)
  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)
  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)
  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)
  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" } });
  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 });
  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!"); };
  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);
  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();
  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();
  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();
  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
  76. 36

  77. 37

  78. 38

  79. 39

  80. 40

  81. 41

  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")}); } });
  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;
  84. WHAT SHOULD A TODO LOOK LIKE? 44 <script type="text/template" id="item-template">

    <div class="todo <%= done ? 'done' : '' %>"> <div class="display"> <input class="check" type="checkbox" <%= done ? 'checked="checked"' : '' %> /> <div class="todo-text"></div> <span class="todo-destroy"></span> </div> <div class="edit"> <input class="todo-input" type="text" value="" /> </div> </div> </script>
  85. DISPLAY STATISTICS & CLEAR BUTTON 45 <script type="text/template" id="stats-template"> <%

    if (total) { %> <span class="todo-count"> <span class="number"><%= remaining %></span> <span class="word"><%= remaining == 1 ? 'item' : 'items' %></span> left. </span> <% } %> <% if (done) { %> <span class="todo-clear"> <a href="#"> Clear <span class="number-done"><%= done %></span> completed <span class="word-done"><%= done == 1 ? 'item' : 'items' %></span> </a> </span> <% } %> </script>
  86. PUT IT ALL INTO A STATIC SKELETON 46 <div id="todoapp">

    <div class="title"> <h1>Todos</h1> </div> <div class="content"> <div id="create-todo"> <input id="new-todo" placeholder="What needs to be done?" type="text" /> </div> <div id="todos"> <ul id="todo-list"></ul> </div> <div id="todo-stats"></div> </div> </div>
  87. 47

  88. 47 NO BEHAVIOR YET.

  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 ... */
  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(); }
  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 ... */
  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; }
  93. 52 HTTP://LOCALHOST:9000/TODO DEMO.

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

    NEED ...
  95. REALTIME!?!?

  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.
  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
  98. 56 HTTP://TODOS.HANNOVERJS.DE/ BUT: A DIRTY LITTLE HACK ...

  99. 56 HTTP://TODOS.HANNOVERJS.DE/ BUT: A DIRTY LITTLE HACK ... TOO MUCH

    TRAFFIC EDITING IMPOSSIBLE VIEW GETS REPLACED BY UPDATES NO CONFLICT HANDLING
  100. 57 SO, WHAT ABOUT MVC & REALTIME?

  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.
  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.
  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.
  104. 59

  105. 60

  106. 61

  107. ADVERTISEMENT! 62

  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
  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