Javascript Application Architecture with Backbone.JS

Post on 17-May-2015

1.881 views 4 download

Tags:

description

Intro to Javascript Application Architecture with Backbone.JS. Overview on Javascript MVC, templating with Underscore.js, and Single-Page Apps.

Transcript of Javascript Application Architecture with Backbone.JS

Javascript Application Architecture with

Min Ming Lo minming@minming.net | @lominming

Agenda

•  Intro

•  What is Backbone.js?

•  The Traditional Web

•  MVC with Backbone.js

•  Templating with Underscore.js

•  Single-Page Apps

•  The Future

Intro

•  Graduated in 2011

•  B.S. and M.S. Computer Science

•  Co-Founder of Pixelapse

•  Full-Stack + Front-end + Design

Github for Designers Version Control + Backup + Collaboration

http://vimeo.com/pixelapse/pixelapse

Pixelapse Stack

•  Web App: Postgres 9.2 + Rails 3.2 (Ruby) + Backbone.js 1.0

•  File Server: Tornado 3.0.1 (Python)

•  Client Apps: Python + (Object-C or Qt)

•  Heroku + Amazon S3 + PubNub

What is Backbone.js?

Backbone.js is many things...

•  Javascript Model-View-Controller (MVC) framework

•  Templating engine (through Underscore.js)

•  Single-page apps (through router & history)

Why Backbone.js?

•  Very flexible

•  can support multiple ways of structuring things; do not have to use everything; tailor to your needs

•  Huge community

•  easy to find resources; get answers; multiple extensions

The Traditional Web

How web app works?

Client Server Database

Display  rendered  HTML  1.  Get  data  from  database  2.  Format  into  HTML  tags  3.  Send  back  to  client  

Holds  data  

The Traditional Way

Client Server Database

<ul>      <li>David</li>      <li>John</li>      <li>Jack</li>      <li>Dan</li>  </ul>  

ID   Name  

1   David  

2   John  

3   Jack  

4   Dan  

...   ...  

<ul>      <%  posts.each  do  |p|  %>          <li><%=  p.name  %></li>      <%  end  %>  </ul>  

• Great for UI only sites. Bad for dynamic interactive web apps.

• E.g. I want to update Dan to Peter

MVC

E.g. Updating Attributes

Server Database

<ul>      <li  id='n1'>David</li>      <li  id='n2'>John</li>      <li  id='n3'>Jack</li>      <li  id='n4'>Dan</li>  </ul>  

ID   Name  

1   David  

2   John  

3   Jack  

4   Dan  

...   ...  

<ul>      <%  posts.each  do  |p|  %>          <li><%=  p.name  %></li>      <%  end  %>  </ul>  

Client

1.  Encode UI with additional information like data-ids

2.  $.ajax(...) call to update ID: 4 with Peter

3.  $(“#n4”).html(“Peter”)���

E.g. Creating Items

Server Database

<ul>      <li  id='n1'>David</li>      <li  id='n2'>John</li>      <li  id='n3'>Jack</li>      <li  id='n4'>Dan</li>  </ul>  

ID   Name  

1   David  

2   John  

3   Jack  

4   Dan  

...   ...  

<ul>      <%  posts.each  do  |p|  %>          <li><%=  p.name  %></li>      <%  end  %>  </ul>  

Client

1.  $.ajax(...) call to create new item

2.  $(“ul”).append("<li id='" + data.id + "'>" + data.name + "</li>")���

Duplication of logic

Interactive Web Examples

Web Getting More Interactive

•  The motivation for robust Javascript Application Architecture

•  No callback hell

•  Easy to update UI, easy to update server

•  Examples

•  Gmail, Facebook, Twitter, Pixelapse, etc.

MVC with Backbone.js

Javascript UI

Server Database ID   Name  

1   David  

2   John  

3   Jack  

4   Dan  

...   ...  

<ul>      <%  posts.each  do  |p|  %>          <li><%=  p.name  %></li>      <%  end  %>  </ul>  

Client

1.  Move rendering logic to client-side

2.  Sometimes called Javascript UI because Javascript is doing the templating now (not the server)���

Collection

Collection View

Client-Side MVC

Model Model View

REST API

Client Server Database ID   Name  

1   David  

2   John  

3   Jack  

4   Dan  

...   ...  

<ul>  

<li>name</li>  

.json  var  list  =    

1.  Add/Modify/Delete item

2.  Updates server + updates UI���

MVC

ID   Name  

1   David  

2   John  

3   Jack  

4   Dan  

...   ...  

Collection

Client-Side MVC

Model

REST API

Client Server Database ID   Name  

1   David  

2   John  

3   Jack  

4   Dan  

...   ...  

Let’s us see how Backbone help us update the server when we modify the list���

Collection

Server REST API

Model

REST API

Client Server Database ID   Name  

1   David  

2   John  

3   Jack  

4   Dan  

...   ...  

Web Server API

•  CRUD - Create, Read, Update, Delete

•  Maps to HTTP methods: POST, GET, PUT, DELETE

•  Scaffolding on most modern web frameworks like Rails or Django

RESTful API

Action Method URL

Create POST /posts

Read GET /posts(/:id)

Update PUT /posts/:id

Delete DELETE /posts/:id

Create

var  PostList  =  Backbone.Collection.extend({      model:  PostItem,  //Reference  to  collection's  model      url:  '/posts'  });    post_list  =  new  PostList();    //I  want  to  create  a  new  item  in  the  collection  post_list.create({      title:  'New  Title'  });      //Backbone  will  automatically  request  '/posts'  with  POST  

method  attaching  the  data  

Read

//I  want  to  get  a  list  of  posts  post_list.fetch();    //Backbone  will  automatically  request  '/posts'  with  GET  

method          //I  want  to  get  one  post  item  =  post_list.get(1);  item.fetch();    //Backbone  will  automatically  request  '/posts/1'  with  GET  

method  

Update

var  PostItem  =  Backbone.Model.extend({      urlRoot:  '/posts'  });    //I  want  to  change  some  attributes  of  an  item  (id:  8)  item  =  post_list.get(8);  item.set('title',  'New  Title');  item.save();    //Backbone  will  automatically  request  '/posts/8'  with  PUT  

method  attaching  the  data  

Delete

//I  want  to  change  some  attributes  of  an  item  (id:  8)  item  =  post_list.get(8);  item.destroy();    //Backbone  will  automatically  request  '/posts/8'  with  DELETE  

method  

Quick Summary

Backbone Action Method URL

collection.create() Create POST /posts

collection.fetch() model.fetch() Read GET /posts(/:id)

model.set(...) model.save() Update PUT /posts/:id

model.destroy() Delete DELETE /posts/:id

Collection

Collection View

Client-Side MVC

Model Model View

REST API

Client Server Database ID   Name  

1   David  

2   John  

3   Jack  

4   Dan  

...   ...  

ID   Name  

1   David  

2   John  

3   Jack  

4   Dan  

...   ...   .json  

<ul>  

<li>name</li>  

var  list  =    

1.  Add/Modify/Delete item

2.  Updates server + updates UI���

✓"

✓"

?"

?"

Backbone Events

•  Attributes changes triggers a Backbone Sync event

•  Also triggers ‘change’ event, re-renders UI accordingly

Create

var  PostListView  =  Backbone.View.extend({      el:  "ul",      initialize:  function()  {          this.collection.bind('add',  this.addPostItem);          this.collection.bind('reset',  this.render,  this);    },      render:  function()  {          $(this.el).html("");          this.collection.each(this.addPostItem);          return  this;  },      addPostItem:  function(item)  {          var  post_item_view  =  new  PostItemView({model:  item});          $(this.el).append(post_item_view.render().el);  }  });      //Every  time  an  item  is  ADD-­‐ed  to  the  collection,  

addPostItem()  would  be  called  

Create

post_list.create({      title:  'New  Title'  });      1:  request  '/posts'  with  POST  method  attaching  the  data  //Automatically  tell  the  server  new  item  is  created    2:  addPostItem()  will  automatically  be  called  //Automatically  update  the  UI  to  include  new  item  

Read

var  PostListView  =  Backbone.View.extend({      el:  "ul",      initialize:  function()  {          this.collection.bind('add',  this.addPostItem);          this.collection.bind('reset',  this.render,  this);    },      render:  function()  {          $(this.el).html("");          this.collection.each(this.addPostItem);          return  this;  },      addPostItem:  function(item)  {          var  post_item_view  =  new  PostItemView({model:  item});          $(this.el).append(post_item_view.render().el);  }  });      //Every  time  a  collection  is  RESET-­‐ed,  render()  would  be  

called  

Read

post_list.fetch();  //Backbone  0.9  post_list.fetch({reset:true});  //Backbone  1.0    1:  request  '/posts'  with  GET  method  //Get  list  of  posts  from  server    2:  render()  will  automatically  be  called        loop  through  each  item  (from  the  list  of  posts)        for  each  item,  append  to  the  el  (<ul>)  //Automatically  update  the  UI  with  list  of  items  

Update

var  PostItemView  =  Backbone.View.extend({      tagName:    "li",      initialize:  function()  {          this.model.on('change',  this.render,  this);          this.model.on('destroy',  this.remove,  this);      },      render:  function()  {          $(this.el).html('<h1>'  +  this.model.get('name')  +  '</

h1>');          return  this;      },  });    //Every  time  an  item  has  CHANGE-­‐ed,  render()  would  be  called  

Update

item  =  post_list.get(8);  item.set('title',  'New  Title');  item.save();    1:  request  '/posts/8'  with  PUT  method  attaching  the  data  //Automatically  tell  server  that  item  has  changed    2:  render()  will  automatically  be  called  //Automatically  update  the  UI  to  include  the  changes  

Delete

var  PostItemView  =  Backbone.View.extend({      tagName:    "li",      initialize:  function()  {          this.model.on('change',  this.render,  this);          this.model.on('destroy',  this.remove,  this);      },      remove:  function()  {          $(this.el).fadeOut(300,  function()  {  $

(this).remove();  });      },  });    //Every  time  an  item  is  DESTROY-­‐ed,  remove()  would  be  called  

Delete

item  =  post_list.get(8);  item.destroy();    1:  request  '/posts/8'  with  DELETE  method  //Automatically  tell  server  that  item  has  been  deleted    2:  remove()  will  automatically  be  called  //Automatically  update  the  UI  to  remove  the  item  

Collection

Collection View

Client-Side MVC

Model Model View

REST API

Client Server Database ID   Name  

1   David  

2   John  

3   Jack  

4   Dan  

...   ...  

1.  Add/Modify/Delete item

2.  Updates server + updates UI���

✓"

✓"

✓"

✓"

ID   Name  

1   David  

2   John  

3   Jack  

4   Dan  

...   ...   .json  

<ul>  

<li>name</li>  

var  list  =    

Quick Summary

Backbone Server Sync UI Sync

collection.create() POST “/posts” Triggers “add” on collection

collection.fetch() model.fetch() GET “/posts(/:id)” Triggers “reset” on collection

model.set(...) model.save() PUT “/posts/:id” Triggers “change” on model

model.destroy() DELETE “/posts/:id” Triggers “destroy” on model

Tips & Tricks

•  {silent:true} to prevent event triggers

•  item.set("title", "Silent", {silent: true});

•  {wait: true} if you'd like to wait for the server respond before updating the UI

•  post_list.create({ title: "Waiting..." }, {wait: true})

Templating with Underscore.js

Update

var  PostItemView  =  Backbone.View.extend({      tagName:    "li",      initialize:  function()  {          this.model.on('change',  this.render,  this);          this.model.on('destroy',  this.remove,  this);      },      render:  function()  {          $(this.el).html('<h1>'  +  this.model.get('name')  +  '</

h1>');          return  this;      },  });  

• Ugly and clumsy to write inline html like this

_.template

•  Backbone’s hard dependency

•  Write templates like what you expect from Rails, Django, etc. but on the client-side

•  Interpreted and rendered by the browser

Basic Example

<script  type="text/template"  id="post_item_template">        <h1>{{=  post.get("title")  }}</h1>      <h6>{{=  post.get("name")  }}</h6>      <p>{{=  post.get("content")  }}</p>    </script>  

_ is basically Javascript

<script  type="text/template"  id="post_item_template">        <h1>{{=  post.get("title")  }}</h1>      <h6>{{=  post.get("name")  }}</h6>      <p>{{=  post.get("content")  }}</p>        {{  _.each(post.get("comments"),  function(c,  i)  {  }}          {{  if  (i<6)  {  }}                <p>{{=  c.body  }}</p>            {{  }  }}      {{  });  }}    </script>  

Backbone without _

render:  function()  {      $(this.el).html('<h1>'  +  this.model.get('title')  +  '</

h1>');  }  

Backbone with _

template:  _.template($('#post_item_template').html()),      render:  function()  {      $(this.el).html(this.template(  {  post:  this.model  }  ));  }        <script  type="text/template"  id="post_item_template">      <h1>{{=  post.get("title")  }}</h1>  </script>  

Update with _ template

var  PostItemView  =  Backbone.View.extend({      tagName:    "li",      template:  _.template($('#post_item_template').html()),      initialize:  function()  {          this.model.on('change',  this.render,  this);          this.model.on('destroy',  this.remove,  this);      },      render:  function()  {          $(this.el).html(this.template(  {  post:  this.model  }  ));          return  this;      },  });    //Every  time  an  item  has  CHANGE-­‐ed,  render()  would  be  called,  

and  the  template  will  be  used  

Tips and Tricks

Conflict  with  Rails  templating.  Changed  from    <%=  post.get(“title”)  %>  to  {{=  post.get(“title”)  }}          _.templateSettings  =  {      interpolate:  /\{\{\=(.+?)\}\}/g,      escape:  /\{\{\-­‐(.+?)\}\}/g,      evaluate:  /\{\{(.+?)\}\}/g  };  

Single Page Apps

What is Single Page App?

•  Single Page Apps a.k.a. Complete Javascript UI a.k.a. Single-Page Javascript UI

•  No refresh. Everything is AJAX-ed.

•  Feels more like an app, less like a website (less request/respond feel)

•  E.g. Twitter, Gmail, iCloud, Google Docs

Key Ideas

•  URL should change (so that bookmarks still work/links copy still work)

•  Back button should work

•  Loading UI

•  Javascript handles the routes (the URLs)

Backbone Router

var  AppRouter  =  Backbone.Router.extend({      routes:  {          "posts/:id":  "getPost",          "contact":  "getContact",          "*actions":  "defaultRoute"      },      getPost:  function(id)  {          //execute  stuff  here      },      ...  }    var  app_router  =  new  AppRouter;  app_router.navigate("posts/123",  true);  

HTML5 Push State

•  The old way: location.hash (e.g. #help)

•  Re-write location bar URL

•  Making sure Back Button still works

•  IE 10 onwards

•  http://caniuse.com/#search=history

Backbone.history.start({pushState:  true});  

DEMO

Quick Recap

•  Javascript MVC framework

•  Backbone Sync automatically syncs with your backend

•  And automatically updates your UI

•  Templating engine (through Underscore.js)

•  Single-page apps (through router & history)

The Future...

So... Single-Page Apps are the way to go?

•  Very hard to maintain. Gets complex really fast.

•  Client code can become “heavy” (easily see 2-3x increase in JS file size)

•  SEO will be challenging

•  Have to think about User Experience

•  Not for older browsers

Should you make single-page apps?

•  Who are your users? (modern browsers?)

•  Are there a lot of interactions? Does the whole site needs to be a single app?

•  Does your web app need to be in real-time?

Many other frameworks...

•  AngularJS (by Google; use traditional JS)

•  Ember.js (very structured; convention over configuration)

•  KnockoutJS (two way bindings)

•  Resources:

•  http://coding.smashingmagazine.com/2012/07/27/journey-through-the-javascript-mvc-jungle/

Stack

•  Use well establish frameworks

•  Large community, lots of resources

•  Good conventions (like RESTful, json, etc)

•  Easy to find plug-ins, extensions (less re-write)

•  Recommendations

•  Web frameworks: Rails, Django, (Node.js)

•  Javascript MVC: Backbone (Angular, Ember, Knockout)

Feel free to reach out

•  https://github.com/lominming/rails-backbone-example

•  minming@minming.net

•  @lominming

•  Any Javascript, Backbone stuff

•  General startup stuff