SlideShare a Scribd company logo
Backbone.js: Run your
Application Inside The
Browser
Howard M. Lewis Ship
TWD Consulting
hlship@gmail.com
@hlship
                       © 2012 Howard M. Lewis Ship
Are We Doing it Wrong?
             GET /


                             Server


         <html><head>…



         POST /addCustomer


                             Server


         <html><head>…
Page Refresh
     vs.
    User
 Interaction
Challenges

 Statefulness
   Browser Vs. Server
 HTML
   POST key values vs. heirarchical data
 Avoid full page updates
 ❝Can it be as easy to use as an iPad app?❞
Server's Role
 Load the application:
   Static HTML, images, stylesheets
   JavaScript
 Source/Sink of data
   JSON is the easy way!
   Ties in with NoSQL
 In-place page updates
What's new here?

 Isn't this just Ajax?
   Core structure vs. optional enhancements
 Isn't this just jQuery?
   jQuery is great w/ elements, events, effects
   More structure is needed
http://documentcloud.github.com/backbone/
Dependencies                                       Ba
                                                     ck
                                                   0.9 bon
                                                      .2 e




                       Backbone



   jQuery (or Zepto)       Underscore   JSON2


                                         Not needed in
                                        modern browsers
Structure

         XHR
Server         Sync




                                                 Collection


                       0..n



                               Model Events
                                                                    DOM Events
               Model                               View

                              Property Updates

                                                                      <div>
                                                                        <input ...
                                                      DOM Updates
Underscore.js
Caution: CoffeeScript
Caution: CoffeeScript
 CoffeeScript
 ➠ ❝… a little language that compiles into JavaScript❞
 Concise and readable
 Optional parenthesis
 Implicit returns
 Concise function definitions
 Fits nicely on slides!
CoffeeScript: Invoking Functions
$(".x-cancel").tooltip "hide"             $(".x-cancel").tooltip("hide")




collection.add new Quiz(originalModel),
  at: 0

                                collection.add(new Quiz(originalModel), {
                                  at: 0
                                });
CoffeeScript: Defining Functions
(x,y) -> x * y                                      function (x, y) {
                                                      return x * y;
                                                    }



isBlank = (str) ->                    function isBlank(str) {
  _.isNull(str) or                      return _.isNull(str) ||
  _.isUndefined(str) or                        _.isUndefined(str) ||
  str.trim() is ""                             str.trim() === "";
                                      }


@collection.each (round) ->    this.collection.each(function(round) {
  round.set "index", index++     return round.set("index", index++);
                               });
http://jsconsole.com
http://jsconsole.com


:load underscore
:load coffeescript
:load https://raw.github.com/⏎
documentcloud/backbone/0.9.2/backbone.js
Backbone Events
Backbone.Events


Mixin to be added to any object
Add/remove listeners
Trigger events
Alternative to DOM events
on(eventName, listener, [context])
dispatcher = _.extend {}, Backbone.Events
➠ …
dispatcher.on "gnip", (id) -> console.log "gnip: #{id}"
➠ …
dispatcher.trigger "gnip", 1234
"gnip: 1234"
➠ …
dispatcher.on "all", (eventName, args...) ->
  console.log "Received '#{eventName}'"
➠ …
dispatcher.trigger "fnord"
"Received 'fnord'"
➠ …
dispatcher.trigger "gnip", 54321
"gnip: 54321"
"Received 'gnip'"
➠ …
context and this
 this is a side-effect of method invocation:

 anObject.aMethod() ➠     var fn = anObject["aMethod"];
                          fn.call(anObject)

                              Sets this for new stack frame


 DOM and JQuery manipulate this
 ➠ Usually the DOM element that triggered event
 this not relevant to HOFs
   Functions, parameters, local scope
Backbone.Events

on(), off(), trigger() return this
trigger()
➠ arguments passed to listeners
Event name "all"
➠ Listener sees all events,
  first parameter is event name
off([event], [callback], [context])

 Removes matching listeners
 event ➠ event to remove, or null for all
 callback ➠ callback function to match
 context ➠ context to match
 off() ➠ remove all listeners
 off("gnip") ➠ remove all 'gnip' listeners
Backbone Models
Backbone.Model


Base class via extend()
Triggers events on property changes
Validates changes for consistency
CRUD via Backbone.Sync
Quizzical Empire
Paperwork
HTTP / XHR




Backbone                     Express
Underscore                    Jade
  jQuery                    Mongoose
 Mustache                 connect-assets
 Bootstrap                  Underscore
Backbone.js: Run your Application Inside The Browser
Backbone.Model
                                                              id
                                                          idAttribute
                                                             cid
                                                          extend()
                                                            get()
                                                            set()
                                                          escape()
                                                           fetch()
                                                           save()
                                                          destroy()
                                                          defaults()




        Quiz
                                 Round                  Question
enableSave() : boolean
                         enableSave() : boolean   enableSave() : boolean
          _id
          title                  kind                      text
       location                  title                   answer
       created                 questions                  value
        rounds
Models and Attributes
 Models contain attributes … data from server
   Nothing is pre-defined
 One attribute uniquely identifies the Model
   idAttribute property defines this
   idAttribute is "_id" for MongoDB
 id property shadows the id attribute
 cid property ➠ temporary unique id before saved to
 server
extend(properties, [class props])



 Creates a new type of model
 Properties used to define / override new methods
extend(properties, [class props])
             Model = Backbone.Model
    Quiz = Model.extend
      idAttribute: "_id"
      urlRoot: "/api/quiz"
      parse: (response) ->
        response.rounds = _(response.rounds).map (raw) ->
          new Round raw, { parse: true }
        return response

      default: ->
        rounds: []

      enableSave: ->
        (not isBlank @get "title") and
        _(@get "rounds").all (round) -> round.enableSave()
Creating an Instance

Quiz = Backbone.Model.extend
  initialize: ->
    console.log "Quiz initialized with", @attributes
➠ …
new Quiz
  title: "New Quiz"
  location: "Portland, OR"
"Quiz initialized with"
{"location": "Portland, OR", "title": "New Quiz"}
➠ …
Simple Defaults

 Quiz = Backbone.Model.extend
   initialize: ->
     console.log "Quiz initialized with", @attributes
   defaults:
     location: "Default Location"
 ➠ …
 new Quiz
   title: "Simple Defaults Quiz"
 "Quiz initialized with"
 {"location": "Default Location", "title": "Simple
 Defaults Quiz"}
 ➠ …
Computed Defaults

 Quiz = Backbone.Model.extend
   initialize: ->
     console.log "Quiz initialized with", @attributes
   defaults: ->
     title: "New Quiz at #{new Date().toDateString()}"
 ➠ …
 new Quiz
 "Quiz initialized with"
 {"title": "New Quiz at Mon Apr 09 2012"}
 ➠ …
Reading Attributes

quiz = new Quiz title: "<Script>ing Attack"
"Quiz initialized with"
                           jsconsole bug!
{"title": "
➠ …
quiz.get "title"
➠ "<Script>ing Attack"
quiz.escape "title"
➠ "&lt;Script&gt;ing Attack"
Changing Attributes

quiz.set "title", "New Title"    attribute key and value
➠ …
quiz.attributes
➠ {"title": "New Title"}
quiz.set location: "New Location"           keys and values
➠ …
quiz.attributes
➠ {"location": "New Location",
    "title":"New Title"}
Attribute Change Notification

 quiz.on "all", (name) -> console.log "Event: #{name}"
 ➠ …
 quiz.set title: "Updated Title"
 "Event: change:title"
 "Event: change"
 quiz.set { location: "Updated Location"}, silent:true
 ➠ …
 quiz.change()
 ➠ …
 "Event: change:location"
 "Event: change"
 ➠ …
Validation

Quiz = Backbone.Model.extend
  validate: (attributes) ->
    return "Title may not be blank" if isBlank attributes.title
➠ …
quiz = new Quiz
➠ …
quiz.isValid()
➠ false
quiz.set title:null
➠ false
quiz.set title:"Valid Title"
➠ …
quiz.isValid()
➠ true




                NOT recommended for user input validation
Backbone Views
Backbone Views

Are Controllers not Views
Create the UI for the element
  Handle model events, update DOM
  Handle DOM events, update model
  Trigger own events
View.extend(properties, [class properties])
 Properties are added to   Standard properties:
 the View
                             model
 Defaults:
                             collection
   tagName ➠ "div"
                             el
                             id
                             attributes
                             className
                             tagName
View and Element
        @el is CoffeeScript for this.el


 @el ➠ the element for this View
   passed as an option to the constructor
   or created from tagName, className, id, and
   attributes options
 Initially detached
   @render or @initialize inserts it into DOM
Constructor

 new MyView([options])
  Merges options into @options
  Creates @el if not passed as an option
  Passes all constructor arguments to @initialize
  Delegates events to @el
Event Delegation
 Automatically dispatch events from inside @el
                   QuizTableRowView = View.extend
                     tagName: "tr"

                     initialize: -> …

  event name and     events:
                       "click .x-delete": "deleteDialog"
     selector
                       "click .x-edit": "editQuiz"

                     render: -> …
                                                     method name
                    deleteDialog: -> …

                    editQuiz: -> …




                           .x-verb: Personal style
@render vs. @initialize()

 Default @render implementation is: return this
 Goal: minimize DOM reflow/recalc
   Sub-views attach their @el inside container's
   detached @el
   Top-most @el attached once
 Also OK to attach @el inside @initialize
Uh … attach?

    QuizEditorView = FormView.extend

      className: "tab-pane"

      initialize: ->
        …
                            Text
          @$el.attr("id", tabId)
            .html(readTemplate "QuizEditorView")
            .appendTo("#top-level-tabs > .tab-content")

      …
                       Detached @el attached to DOM
Elements and jQuery
 @$el ➠ jQuery reference to the element
 ➠ @$el.html "<div>This View's Content</div>"
 $ ➠ Equivalent to @$el.find
 setElement(element, delegate)
   Sets view's @el
   Undelegates events from old @el
   Delegates events to new @el
Client-Side Templating
                     id aligns with View
layout.jade
script#ConfirmDialog(type="text/template")
  .modal-header
    a.close(data-dismiss="modal") &times;
    h3 {{title}}
  .modal-body {{{body}}}
  .modal-footer
    button.btn.btn-danger.x-confirm(data-dismiss="modal") {{label}}
    button.btn(data-dismiss="modal") Cancel




Rendered HTML
<script id="ConfirmDialog" type="text/template"><div class="modal-
header"><a class="close" … </script>
Mustache
    readTemplate = (scriptId) -> $("##{scriptId}").html()

    fromMustacheTemplate = (scriptId, attributes) ->
      Mustache.render readTemplate(scriptId), attributes




Mustache.render(string, attributes)
 Parses the string
 {{name}} inserts escaped attribute
 {{{name}}} inserts un-escaped attribute
 Result ready to insert into DOM
Putting It All Together – Reusable Confirmation Dialog
ConfirmDialog = View.extend

  className: "modal fade in"

  initialize: ->
    @$el.html fromMustacheTemplate "ConfirmDialog",
      title: @options.title
      body: @options.body
      label: @options.label or "Confirm"

    @$(".x-confirm").addClass @options.buttonClass or "btn-primary"

    $("body").append(@$el)

    @$el.modal().on "hidden", =>
      @remove()

   doConfirm: ->
     @trigger "confirm"

   events:
    "click .x-confirm": "doConfirm"
Using ConfirmDialog

    deleteDialog: ->

      title = @model.escape "title"

      dialog = new ConfirmDialog
        title: "Really delete Quiz?"
        body: "<p>Deletion of quiz <strong>#{title}</strong>
   is immediate and can not be undone.</p>"
        label: "Delete Quiz"
        buttonClass: "btn-danger"

      dialog.on "confirm", => @model.destroy()


               If the user clicks Delete Quiz, then
                           destroy model
ConfirmDialog = View.extend

  className: "modal fade in"

  initialize: ->
    @$el.html fromMustacheTemplate "ConfirmDialog",
      title: @options.title
      body: @options.body
      label: @options.label or "Confirm"

    @$(".x-confirm").addClass @options.buttonClass or "btn-primary"

    $("body").append(@$el)            Update body of @el from template
    @$el.modal().on "hidden", =>        Modify the class of the button
      @remove()

   doConfirm: ->
     @trigger "confirm"

   events:
    "click .x-confirm": "doConfirm"
ConfirmDialog = View.extend

  className: "modal fade in"

  initialize: ->
    @$el.html fromMustacheTemplate "ConfirmDialog",
      title: @options.title
      body: @options.body
      label: @options.label or "Confirm"

    @$(".x-confirm").addClass @options.buttonClass or "btn-primary"

    $("body").append(@$el)            Insert elements into the DOM
    @$el.modal().on "hidden", =>
      @remove()

   doConfirm: ->
     @trigger "confirm"

   events:
    "click .x-confirm": "doConfirm"
ConfirmDialog = View.extend

  className: "modal fade in"

  initialize: ->
    @$el.html fromMustacheTemplate "ConfirmDialog",
      title: @options.title
      body: @options.body
      label: @options.label or "Confirm"

    @$(".x-confirm").addClass @options.buttonClass or "btn-primary"

    $("body").append(@$el)

    @$el.modal().on "hidden", =>
      @remove()
                                   Remove the element from the DOM
   doConfirm: ->                     after hide animation completes
     @trigger "confirm"

   events:
    "click .x-confirm": "doConfirm"
ConfirmDialog = View.extend

  className: "modal fade in"

  initialize: ->
    @$el.html fromMustacheTemplate "ConfirmDialog",
      title: @options.title
      body: @options.body
      label: @options.label or "Confirm"

    @$(".x-confirm").addClass @options.buttonClass or "btn-primary"

    $("body").append(@$el)

    @$el.modal().on "hidden", =>
      @remove()

   doConfirm: ->               Delegate a listener to the main button
     @trigger "confirm"

   events:
    "click .x-confirm": "doConfirm"
Form Views
QuizFieldsEditorView
   QuizFieldsEditorView = FormView.extend
     initialize: ->
       @$el.html readTemplate "QuizFieldsEditorView"

       @linkField name for name in ["title", "location"]




   script#QuizFieldsEditorView(type="text/template")
     .control-group.x-title
       label Quiz Title
       .controls
         input.span4(type="text", required)
         span.help-inline Title or theme for the Quiz
     .control-group.x-location
       label Location
       input.span4(type="text")
       span.help-inline Location where Quiz will take place
FormView
  FormView = View.extend

    linkField: (name, selector = ".x-#{name}") ->
      $field = @$("#{selector} input")

      $field.val @model.get(name)

      $field.on "change", (event) =>
        newValue = event.target.value
        @model.set name, newValue

    linkElement: (name, selector = ".x-#{name}", defaultText) ->
      element = @$(selector)
      update = =>
        element.html (@model.escape name) or defaultText

      update()

      @model.on "change:#{name}", update
Backbone Collections
Collection

 Contains Models
 Has its own URL for queries & updates
 May represent a subset of the data
 Fires own events
 Forwards events from Models
 Can create new Model instances
Collection Access
 Implements Underscore collection methods (pluck, map, etc.)
 get(id) ➠ Get by unique id
 getByCid(cid) ➠ Get by temporary client id
 at(index) ➠ Get by index in collection
 push(model) ➠ Add to end of collection
 unshift(model) ➠ Add to start of collection
 pop() ➠ Remove and return last model
 shift() ➠ Remove and return first model
 length ➠ Count of models in collection
Collection Events


 add ➠ Model added to Collection
 remove ➠ Model removed from Collection
 reset ➠ Collection's entire contents updated
 change ➠ Model's attributes have changed
 destroy ➠ Model is destroyed (deleted)
QuizTableRowView




     QuizTableView
QuizList
QuizList = Collection.extend
  model: Quiz                  Server returns just _id, title, location,
  url: "/api/quizzes"              created in descending order
QuizTableView
          Alert visible when the QuizList is empty
.alert
  strong.
    No Quizzes have been created yet ... feel free to start creating some!
.limit-height
  table.table.table-bordered.table-striped.table-condensed
    thead
       tr
          th Title
          th.span3 Location
          th.span3 Created
          th.span3
    tbody               QuizTableRowView added as children
.well
  button.btn.btn-primary.x-create-new Create New Quiz
  &nbsp;
  button.btn.x-create-test-data(data-loading-text="Loading ...").
    Create Test Quizzes
QuizTableView
QuizTableView = View.extend

  initialize: ->

      @quizzes = new QuizList

      @$el.html readTemplate "QuizTableView"

      @$(".alert, table").hide()

      @quizzes.on "reset", @addAll, this
      @quizzes.on "add", @addOne, this
      @quizzes.on "destroy", @render, this

      @quizzes.fetch()

      @$(".x-create-test-data").popover
        title: "For Testing"
        content: "Creates many Quizzes with random text, for testing purposes.
                  This will be removed in the final application."

  …
QuizTableView.render()


          render: ->
            if @quizzes.length is 0
              @$(".alert").show()
              @$("table").hide()
            else
              @$(".alert").hide()
              @$("table").show()

            this
QuizTableView – Adding Models
                                         @quizzes.on "reset", @addAll, this
                                         @quizzes.on "add", @addOne, this



addOne: (quiz, collection, options) ->
  view = new QuizTableRowView
    model: quiz
    collection: @quizzes

  # Special case for inserting at index 0, which happens when a new quiz
  # is added in the UI.
  fname = if options? and options.index is 0 then "prepend" else "append"

  @$("tbody")[fname] view.render().el

addAll: (quizzes) ->
  @$("tbody").empty()

  quizzes.each (quiz) => @addOne quiz
  @render()
QuizTableRowView
                                  view = new QuizTableRowView
                                    model: quiz
                                    collection: @quizzes



   QuizTableRowView = View.extend
     tagName: "tr"

     initialize: ->
       @model.on "change", @render, this
       @model.on "destroy", @remove, this

         @template = readTemplate "QuizTableRowView"

     render: ->

         @$el.html Mustache.render @template,
           title: @model.escape "title" or "<em>No Title</em>"
           location: @model.escape "location"
           created: @model.get "created"

     …
Dealing With Relationships
Quiz
enableSave() : boolean
          _id
          title
       location
       created           0..n
        rounds                          Round
                                enableSave() : boolean
                                        kind
                                        title            0..n
                                      questions                       Question
                                                                enableSave() : boolean
                                                                         text
                                                                       answer
                                                                        value
❝Backbone doesn't include
   direct support for nested
   models and collections or
   "has many" associations
 because there are a number
of good patterns for modeling
 structured data on the client
  side, and Backbone should
  provide the foundation for
 implementing any of them.❞
Quiz inside MongoDB

   { "title" : "NFJS",
     "location" : "San Antonio",
     "_id" : ObjectId("4f835669c34c2a9c6f000003"),
     "rounds" : [ #
        { "kind" : "normal", #
          "title" : "Server-Side Java", #
           "_id" : ObjectId("4f835669c34c2a9c6f000004"),
   #
           "questions" : [ ] }
     ],
     "created" : ISODate("2012-04-09T21:36:41.726Z")
   }
Quiz.parse()
    Quiz = Model.extend
      idAttribute: "_id"

      urlRoot: "/api/quiz"

      parse: (response) ->
        response.rounds = _(response.rounds).map (raw) ->
          new Round raw, { parse: true }
        return response

      default: ->
        rounds: [] # of Round

      enableSave: ->
        (not isBlank @get "title") and
        _(@get "rounds").all (round) -> round.enableSave()
Round.parse()

   Round = Model.extend
     default: ->
       questions: [] # of Question

     parse: (response) ->
       response.questions = _(response.questions).map (raw) ->
         new Question raw, { parse: true }
       return response

     enableSave: ->
       return false if isBlank @get "title"

       questions = @get "questions"

       _.all questions, (q) -> q.enableSave()
Managing Relationships

QuizRoundsEditorView = View.extend
  initialize: ->
                                Build collection from simple array
    …

    @collection = new RoundCollection @model.get "rounds"

    @collection.on "all", =>
      @model.set "rounds", @collection.toArray()
      @model.trigger "childChange"

                           Update array from collection on
                                    any change



                                       RoundCollection = Collection.extend
                                         model: Round
Wrap Up
Backbone



Client-centered design
Not Covered

 Lots!
 Details of Sync
 Router ➠ encode state into URL
 Server-side details
 Lots of libraries on top of Backbone
http://knockoutjs.com/
http://spinejs.com/
http://howardlewisship.com
Q&A

More Related Content

What's hot (20)

PPTX
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Domenic Denicola
 
PDF
はじめてのGroovy
Tsuyoshi Yamamoto
 
PDF
Singletons in PHP - Why they are bad and how you can eliminate them from your...
go_oh
 
PDF
GeeCON Prague 2014 - Metaprogramming with Groovy
Iván López Martín
 
PDF
Source Code for Dpilot
Nidhi Chauhan
 
PDF
Dpilot Source Code With ScreenShots
DeepAnshu Sharma
 
PDF
Rails on Oracle 2011
Raimonds Simanovskis
 
PDF
Python client api
dreampuf
 
PDF
Say It With Javascript
Giovanni Scerra ☃
 
PDF
Design Patterns Reconsidered
Alex Miller
 
PDF
The state of your own hypertext preprocessor
Alessandro Nadalin
 
PDF
G*ワークショップ in 仙台 Grails(とことん)入門
Tsuyoshi Yamamoto
 
PDF
Node.js in action
Simon Su
 
PDF
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Thomas Fuchs
 
PDF
Xlab #1: Advantages of functional programming in Java 8
XSolve
 
PDF
Models and Service Layers, Hemoglobin and Hobgoblins
Ross Tuck
 
PDF
Обзор фреймворка Twisted
Maxim Kulsha
 
PDF
Command Bus To Awesome Town
Ross Tuck
 
PPTX
ES6 Overview
Bruno Scopelliti
 
PPTX
JavaScript - i och utanför webbläsaren (2010-03-03)
Anders Jönsson
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Domenic Denicola
 
はじめてのGroovy
Tsuyoshi Yamamoto
 
Singletons in PHP - Why they are bad and how you can eliminate them from your...
go_oh
 
GeeCON Prague 2014 - Metaprogramming with Groovy
Iván López Martín
 
Source Code for Dpilot
Nidhi Chauhan
 
Dpilot Source Code With ScreenShots
DeepAnshu Sharma
 
Rails on Oracle 2011
Raimonds Simanovskis
 
Python client api
dreampuf
 
Say It With Javascript
Giovanni Scerra ☃
 
Design Patterns Reconsidered
Alex Miller
 
The state of your own hypertext preprocessor
Alessandro Nadalin
 
G*ワークショップ in 仙台 Grails(とことん)入門
Tsuyoshi Yamamoto
 
Node.js in action
Simon Su
 
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Thomas Fuchs
 
Xlab #1: Advantages of functional programming in Java 8
XSolve
 
Models and Service Layers, Hemoglobin and Hobgoblins
Ross Tuck
 
Обзор фреймворка Twisted
Maxim Kulsha
 
Command Bus To Awesome Town
Ross Tuck
 
ES6 Overview
Bruno Scopelliti
 
JavaScript - i och utanför webbläsaren (2010-03-03)
Anders Jönsson
 

Similar to Backbone.js: Run your Application Inside The Browser (20)

PDF
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Tsuyoshi Yamamoto
 
PDF
前端MVC 豆瓣说
Ting Lv
 
PDF
Web Components With Rails
Boris Nadion
 
PPTX
Rapid and Scalable Development with MongoDB, PyMongo, and Ming
Rick Copeland
 
PDF
Understanding backbonejs
Nick Lee
 
PDF
Backbone.js — Introduction to client-side JavaScript MVC
pootsbook
 
PDF
Green dao
Droidcon Berlin
 
PDF
The Ring programming language version 1.7 book - Part 48 of 196
Mahmoud Samir Fayed
 
PDF
Secrets of JavaScript Libraries
jeresig
 
PDF
Overview of The Scala Based Lift Web Framework
IndicThreads
 
PDF
Scala based Lift Framework
vhazrati
 
PDF
Overview Of Lift Framework
Xebia IT Architects
 
PDF
Backbone Basics with Examples
Sergey Bolshchikov
 
KEY
Jython: Python para la plataforma Java (EL2009)
Leonardo Soto
 
PDF
Latinoware
kchodorow
 
PDF
droidparts
Droidcon Berlin
 
PDF
Writing Maintainable JavaScript
Andrew Dupont
 
KEY
[Coscup 2012] JavascriptMVC
Alive Kuo
 
PDF
jQuery Rescue Adventure
Allegient
 
PDF
DataMapper
Yehuda Katz
 
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Tsuyoshi Yamamoto
 
前端MVC 豆瓣说
Ting Lv
 
Web Components With Rails
Boris Nadion
 
Rapid and Scalable Development with MongoDB, PyMongo, and Ming
Rick Copeland
 
Understanding backbonejs
Nick Lee
 
Backbone.js — Introduction to client-side JavaScript MVC
pootsbook
 
Green dao
Droidcon Berlin
 
The Ring programming language version 1.7 book - Part 48 of 196
Mahmoud Samir Fayed
 
Secrets of JavaScript Libraries
jeresig
 
Overview of The Scala Based Lift Web Framework
IndicThreads
 
Scala based Lift Framework
vhazrati
 
Overview Of Lift Framework
Xebia IT Architects
 
Backbone Basics with Examples
Sergey Bolshchikov
 
Jython: Python para la plataforma Java (EL2009)
Leonardo Soto
 
Latinoware
kchodorow
 
droidparts
Droidcon Berlin
 
Writing Maintainable JavaScript
Andrew Dupont
 
[Coscup 2012] JavascriptMVC
Alive Kuo
 
jQuery Rescue Adventure
Allegient
 
DataMapper
Yehuda Katz
 
Ad

More from Howard Lewis Ship (16)

PDF
Spock: A Highly Logical Way To Test
Howard Lewis Ship
 
PDF
Modern Application Foundations: Underscore and Twitter Bootstrap
Howard Lewis Ship
 
KEY
Have Your Cake and Eat It Too: Meta-Programming Techniques for Java
Howard Lewis Ship
 
PDF
Clojure: Towards The Essence Of Programming (What's Next? Conference, May 2011)
Howard Lewis Ship
 
PDF
Arduino: Open Source Hardware Hacking from the Software Nerd Perspective
Howard Lewis Ship
 
PDF
Practical Clojure Programming
Howard Lewis Ship
 
PDF
Clojure: Towards The Essence of Programming
Howard Lewis Ship
 
PDF
Codemash-Clojure.pdf
Howard Lewis Ship
 
PDF
Codemash-Tapestry.pdf
Howard Lewis Ship
 
PDF
Tapestry 5: Java Power, Scripting Ease
Howard Lewis Ship
 
PDF
Brew up a Rich Web Application with Cappuccino
Howard Lewis Ship
 
PDF
Clojure Deep Dive
Howard Lewis Ship
 
PDF
Clojure: Functional Concurrency for the JVM (presented at OSCON)
Howard Lewis Ship
 
PDF
Cascade
Howard Lewis Ship
 
PDF
Tapestry: State of the Union
Howard Lewis Ship
 
ZIP
Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)
Howard Lewis Ship
 
Spock: A Highly Logical Way To Test
Howard Lewis Ship
 
Modern Application Foundations: Underscore and Twitter Bootstrap
Howard Lewis Ship
 
Have Your Cake and Eat It Too: Meta-Programming Techniques for Java
Howard Lewis Ship
 
Clojure: Towards The Essence Of Programming (What's Next? Conference, May 2011)
Howard Lewis Ship
 
Arduino: Open Source Hardware Hacking from the Software Nerd Perspective
Howard Lewis Ship
 
Practical Clojure Programming
Howard Lewis Ship
 
Clojure: Towards The Essence of Programming
Howard Lewis Ship
 
Codemash-Clojure.pdf
Howard Lewis Ship
 
Codemash-Tapestry.pdf
Howard Lewis Ship
 
Tapestry 5: Java Power, Scripting Ease
Howard Lewis Ship
 
Brew up a Rich Web Application with Cappuccino
Howard Lewis Ship
 
Clojure Deep Dive
Howard Lewis Ship
 
Clojure: Functional Concurrency for the JVM (presented at OSCON)
Howard Lewis Ship
 
Tapestry: State of the Union
Howard Lewis Ship
 
Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)
Howard Lewis Ship
 
Ad

Recently uploaded (20)

PDF
“Squinting Vision Pipelines: Detecting and Correcting Errors in Vision Models...
Edge AI and Vision Alliance
 
PPTX
Digital Circuits, important subject in CS
contactparinay1
 
PDF
Book industry state of the nation 2025 - Tech Forum 2025
BookNet Canada
 
PDF
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
PPTX
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
PDF
Peak of Data & AI Encore AI-Enhanced Workflows for the Real World
Safe Software
 
DOCX
Python coding for beginners !! Start now!#
Rajni Bhardwaj Grover
 
PDF
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
PDF
Go Concurrency Real-World Patterns, Pitfalls, and Playground Battles.pdf
Emily Achieng
 
PDF
Transforming Utility Networks: Large-scale Data Migrations with FME
Safe Software
 
PDF
CIFDAQ Market Wrap for the week of 4th July 2025
CIFDAQ
 
PDF
Transcript: Book industry state of the nation 2025 - Tech Forum 2025
BookNet Canada
 
PDF
Kit-Works Team Study_20250627_한달만에만든사내서비스키링(양다윗).pdf
Wonjun Hwang
 
PDF
UPDF - AI PDF Editor & Converter Key Features
DealFuel
 
PDF
Staying Human in a Machine- Accelerated World
Catalin Jora
 
PDF
Bitcoin for Millennials podcast with Bram, Power Laws of Bitcoin
Stephen Perrenod
 
PDF
Mastering Financial Management in Direct Selling
Epixel MLM Software
 
PPTX
Seamless Tech Experiences Showcasing Cross-Platform App Design.pptx
presentifyai
 
PPTX
Mastering ODC + Okta Configuration - Chennai OSUG
HathiMaryA
 
PDF
NLJUG Speaker academy 2025 - first session
Bert Jan Schrijver
 
“Squinting Vision Pipelines: Detecting and Correcting Errors in Vision Models...
Edge AI and Vision Alliance
 
Digital Circuits, important subject in CS
contactparinay1
 
Book industry state of the nation 2025 - Tech Forum 2025
BookNet Canada
 
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
Peak of Data & AI Encore AI-Enhanced Workflows for the Real World
Safe Software
 
Python coding for beginners !! Start now!#
Rajni Bhardwaj Grover
 
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
Go Concurrency Real-World Patterns, Pitfalls, and Playground Battles.pdf
Emily Achieng
 
Transforming Utility Networks: Large-scale Data Migrations with FME
Safe Software
 
CIFDAQ Market Wrap for the week of 4th July 2025
CIFDAQ
 
Transcript: Book industry state of the nation 2025 - Tech Forum 2025
BookNet Canada
 
Kit-Works Team Study_20250627_한달만에만든사내서비스키링(양다윗).pdf
Wonjun Hwang
 
UPDF - AI PDF Editor & Converter Key Features
DealFuel
 
Staying Human in a Machine- Accelerated World
Catalin Jora
 
Bitcoin for Millennials podcast with Bram, Power Laws of Bitcoin
Stephen Perrenod
 
Mastering Financial Management in Direct Selling
Epixel MLM Software
 
Seamless Tech Experiences Showcasing Cross-Platform App Design.pptx
presentifyai
 
Mastering ODC + Okta Configuration - Chennai OSUG
HathiMaryA
 
NLJUG Speaker academy 2025 - first session
Bert Jan Schrijver
 

Backbone.js: Run your Application Inside The Browser

  • 1. Backbone.js: Run your Application Inside The Browser Howard M. Lewis Ship TWD Consulting [email protected] @hlship © 2012 Howard M. Lewis Ship
  • 2. Are We Doing it Wrong? GET / Server <html><head>… POST /addCustomer Server <html><head>…
  • 3. Page Refresh vs. User Interaction
  • 4. Challenges Statefulness Browser Vs. Server HTML POST key values vs. heirarchical data Avoid full page updates ❝Can it be as easy to use as an iPad app?❞
  • 5. Server's Role Load the application: Static HTML, images, stylesheets JavaScript Source/Sink of data JSON is the easy way! Ties in with NoSQL In-place page updates
  • 6. What's new here? Isn't this just Ajax? Core structure vs. optional enhancements Isn't this just jQuery? jQuery is great w/ elements, events, effects More structure is needed
  • 8. Dependencies Ba ck 0.9 bon .2 e Backbone jQuery (or Zepto) Underscore JSON2 Not needed in modern browsers
  • 9. Structure XHR Server Sync Collection 0..n Model Events DOM Events Model View Property Updates <div> <input ... DOM Updates
  • 12. Caution: CoffeeScript CoffeeScript ➠ ❝… a little language that compiles into JavaScript❞ Concise and readable Optional parenthesis Implicit returns Concise function definitions Fits nicely on slides!
  • 13. CoffeeScript: Invoking Functions $(".x-cancel").tooltip "hide" $(".x-cancel").tooltip("hide") collection.add new Quiz(originalModel), at: 0 collection.add(new Quiz(originalModel), { at: 0 });
  • 14. CoffeeScript: Defining Functions (x,y) -> x * y function (x, y) { return x * y; } isBlank = (str) -> function isBlank(str) { _.isNull(str) or return _.isNull(str) || _.isUndefined(str) or _.isUndefined(str) || str.trim() is "" str.trim() === ""; } @collection.each (round) -> this.collection.each(function(round) { round.set "index", index++ return round.set("index", index++); });
  • 16. http://jsconsole.com :load underscore :load coffeescript :load https://raw.github.com/⏎ documentcloud/backbone/0.9.2/backbone.js
  • 18. Backbone.Events Mixin to be added to any object Add/remove listeners Trigger events Alternative to DOM events
  • 19. on(eventName, listener, [context]) dispatcher = _.extend {}, Backbone.Events ➠ … dispatcher.on "gnip", (id) -> console.log "gnip: #{id}" ➠ … dispatcher.trigger "gnip", 1234 "gnip: 1234" ➠ … dispatcher.on "all", (eventName, args...) -> console.log "Received '#{eventName}'" ➠ … dispatcher.trigger "fnord" "Received 'fnord'" ➠ … dispatcher.trigger "gnip", 54321 "gnip: 54321" "Received 'gnip'" ➠ …
  • 20. context and this this is a side-effect of method invocation: anObject.aMethod() ➠ var fn = anObject["aMethod"]; fn.call(anObject) Sets this for new stack frame DOM and JQuery manipulate this ➠ Usually the DOM element that triggered event this not relevant to HOFs Functions, parameters, local scope
  • 21. Backbone.Events on(), off(), trigger() return this trigger() ➠ arguments passed to listeners Event name "all" ➠ Listener sees all events, first parameter is event name
  • 22. off([event], [callback], [context]) Removes matching listeners event ➠ event to remove, or null for all callback ➠ callback function to match context ➠ context to match off() ➠ remove all listeners off("gnip") ➠ remove all 'gnip' listeners
  • 24. Backbone.Model Base class via extend() Triggers events on property changes Validates changes for consistency CRUD via Backbone.Sync
  • 27. HTTP / XHR Backbone Express Underscore Jade jQuery Mongoose Mustache connect-assets Bootstrap Underscore
  • 29. Backbone.Model id idAttribute cid extend() get() set() escape() fetch() save() destroy() defaults() Quiz Round Question enableSave() : boolean enableSave() : boolean enableSave() : boolean _id title kind text location title answer created questions value rounds
  • 30. Models and Attributes Models contain attributes … data from server Nothing is pre-defined One attribute uniquely identifies the Model idAttribute property defines this idAttribute is "_id" for MongoDB id property shadows the id attribute cid property ➠ temporary unique id before saved to server
  • 31. extend(properties, [class props]) Creates a new type of model Properties used to define / override new methods
  • 32. extend(properties, [class props]) Model = Backbone.Model Quiz = Model.extend idAttribute: "_id" urlRoot: "/api/quiz" parse: (response) -> response.rounds = _(response.rounds).map (raw) -> new Round raw, { parse: true } return response default: -> rounds: [] enableSave: -> (not isBlank @get "title") and _(@get "rounds").all (round) -> round.enableSave()
  • 33. Creating an Instance Quiz = Backbone.Model.extend initialize: -> console.log "Quiz initialized with", @attributes ➠ … new Quiz title: "New Quiz" location: "Portland, OR" "Quiz initialized with" {"location": "Portland, OR", "title": "New Quiz"} ➠ …
  • 34. Simple Defaults Quiz = Backbone.Model.extend initialize: -> console.log "Quiz initialized with", @attributes defaults: location: "Default Location" ➠ … new Quiz title: "Simple Defaults Quiz" "Quiz initialized with" {"location": "Default Location", "title": "Simple Defaults Quiz"} ➠ …
  • 35. Computed Defaults Quiz = Backbone.Model.extend initialize: -> console.log "Quiz initialized with", @attributes defaults: -> title: "New Quiz at #{new Date().toDateString()}" ➠ … new Quiz "Quiz initialized with" {"title": "New Quiz at Mon Apr 09 2012"} ➠ …
  • 36. Reading Attributes quiz = new Quiz title: "<Script>ing Attack" "Quiz initialized with" jsconsole bug! {"title": " ➠ … quiz.get "title" ➠ "<Script>ing Attack" quiz.escape "title" ➠ "&lt;Script&gt;ing Attack"
  • 37. Changing Attributes quiz.set "title", "New Title" attribute key and value ➠ … quiz.attributes ➠ {"title": "New Title"} quiz.set location: "New Location" keys and values ➠ … quiz.attributes ➠ {"location": "New Location", "title":"New Title"}
  • 38. Attribute Change Notification quiz.on "all", (name) -> console.log "Event: #{name}" ➠ … quiz.set title: "Updated Title" "Event: change:title" "Event: change" quiz.set { location: "Updated Location"}, silent:true ➠ … quiz.change() ➠ … "Event: change:location" "Event: change" ➠ …
  • 39. Validation Quiz = Backbone.Model.extend validate: (attributes) -> return "Title may not be blank" if isBlank attributes.title ➠ … quiz = new Quiz ➠ … quiz.isValid() ➠ false quiz.set title:null ➠ false quiz.set title:"Valid Title" ➠ … quiz.isValid() ➠ true NOT recommended for user input validation
  • 41. Backbone Views Are Controllers not Views Create the UI for the element Handle model events, update DOM Handle DOM events, update model Trigger own events
  • 42. View.extend(properties, [class properties]) Properties are added to Standard properties: the View model Defaults: collection tagName ➠ "div" el id attributes className tagName
  • 43. View and Element @el is CoffeeScript for this.el @el ➠ the element for this View passed as an option to the constructor or created from tagName, className, id, and attributes options Initially detached @render or @initialize inserts it into DOM
  • 44. Constructor new MyView([options]) Merges options into @options Creates @el if not passed as an option Passes all constructor arguments to @initialize Delegates events to @el
  • 45. Event Delegation Automatically dispatch events from inside @el QuizTableRowView = View.extend tagName: "tr" initialize: -> … event name and events: "click .x-delete": "deleteDialog" selector "click .x-edit": "editQuiz" render: -> … method name deleteDialog: -> … editQuiz: -> … .x-verb: Personal style
  • 46. @render vs. @initialize() Default @render implementation is: return this Goal: minimize DOM reflow/recalc Sub-views attach their @el inside container's detached @el Top-most @el attached once Also OK to attach @el inside @initialize
  • 47. Uh … attach? QuizEditorView = FormView.extend className: "tab-pane" initialize: -> … Text @$el.attr("id", tabId) .html(readTemplate "QuizEditorView") .appendTo("#top-level-tabs > .tab-content") … Detached @el attached to DOM
  • 48. Elements and jQuery @$el ➠ jQuery reference to the element ➠ @$el.html "<div>This View's Content</div>" $ ➠ Equivalent to @$el.find setElement(element, delegate) Sets view's @el Undelegates events from old @el Delegates events to new @el
  • 49. Client-Side Templating id aligns with View layout.jade script#ConfirmDialog(type="text/template") .modal-header a.close(data-dismiss="modal") &times; h3 {{title}} .modal-body {{{body}}} .modal-footer button.btn.btn-danger.x-confirm(data-dismiss="modal") {{label}} button.btn(data-dismiss="modal") Cancel Rendered HTML <script id="ConfirmDialog" type="text/template"><div class="modal- header"><a class="close" … </script>
  • 50. Mustache readTemplate = (scriptId) -> $("##{scriptId}").html() fromMustacheTemplate = (scriptId, attributes) -> Mustache.render readTemplate(scriptId), attributes Mustache.render(string, attributes) Parses the string {{name}} inserts escaped attribute {{{name}}} inserts un-escaped attribute Result ready to insert into DOM
  • 51. Putting It All Together – Reusable Confirmation Dialog
  • 52. ConfirmDialog = View.extend className: "modal fade in" initialize: -> @$el.html fromMustacheTemplate "ConfirmDialog", title: @options.title body: @options.body label: @options.label or "Confirm" @$(".x-confirm").addClass @options.buttonClass or "btn-primary" $("body").append(@$el) @$el.modal().on "hidden", => @remove() doConfirm: -> @trigger "confirm" events: "click .x-confirm": "doConfirm"
  • 53. Using ConfirmDialog deleteDialog: -> title = @model.escape "title" dialog = new ConfirmDialog title: "Really delete Quiz?" body: "<p>Deletion of quiz <strong>#{title}</strong> is immediate and can not be undone.</p>" label: "Delete Quiz" buttonClass: "btn-danger" dialog.on "confirm", => @model.destroy() If the user clicks Delete Quiz, then destroy model
  • 54. ConfirmDialog = View.extend className: "modal fade in" initialize: -> @$el.html fromMustacheTemplate "ConfirmDialog", title: @options.title body: @options.body label: @options.label or "Confirm" @$(".x-confirm").addClass @options.buttonClass or "btn-primary" $("body").append(@$el) Update body of @el from template @$el.modal().on "hidden", => Modify the class of the button @remove() doConfirm: -> @trigger "confirm" events: "click .x-confirm": "doConfirm"
  • 55. ConfirmDialog = View.extend className: "modal fade in" initialize: -> @$el.html fromMustacheTemplate "ConfirmDialog", title: @options.title body: @options.body label: @options.label or "Confirm" @$(".x-confirm").addClass @options.buttonClass or "btn-primary" $("body").append(@$el) Insert elements into the DOM @$el.modal().on "hidden", => @remove() doConfirm: -> @trigger "confirm" events: "click .x-confirm": "doConfirm"
  • 56. ConfirmDialog = View.extend className: "modal fade in" initialize: -> @$el.html fromMustacheTemplate "ConfirmDialog", title: @options.title body: @options.body label: @options.label or "Confirm" @$(".x-confirm").addClass @options.buttonClass or "btn-primary" $("body").append(@$el) @$el.modal().on "hidden", => @remove() Remove the element from the DOM doConfirm: -> after hide animation completes @trigger "confirm" events: "click .x-confirm": "doConfirm"
  • 57. ConfirmDialog = View.extend className: "modal fade in" initialize: -> @$el.html fromMustacheTemplate "ConfirmDialog", title: @options.title body: @options.body label: @options.label or "Confirm" @$(".x-confirm").addClass @options.buttonClass or "btn-primary" $("body").append(@$el) @$el.modal().on "hidden", => @remove() doConfirm: -> Delegate a listener to the main button @trigger "confirm" events: "click .x-confirm": "doConfirm"
  • 59. QuizFieldsEditorView QuizFieldsEditorView = FormView.extend initialize: -> @$el.html readTemplate "QuizFieldsEditorView" @linkField name for name in ["title", "location"] script#QuizFieldsEditorView(type="text/template") .control-group.x-title label Quiz Title .controls input.span4(type="text", required) span.help-inline Title or theme for the Quiz .control-group.x-location label Location input.span4(type="text") span.help-inline Location where Quiz will take place
  • 60. FormView FormView = View.extend linkField: (name, selector = ".x-#{name}") -> $field = @$("#{selector} input") $field.val @model.get(name) $field.on "change", (event) => newValue = event.target.value @model.set name, newValue linkElement: (name, selector = ".x-#{name}", defaultText) -> element = @$(selector) update = => element.html (@model.escape name) or defaultText update() @model.on "change:#{name}", update
  • 62. Collection Contains Models Has its own URL for queries & updates May represent a subset of the data Fires own events Forwards events from Models Can create new Model instances
  • 63. Collection Access Implements Underscore collection methods (pluck, map, etc.) get(id) ➠ Get by unique id getByCid(cid) ➠ Get by temporary client id at(index) ➠ Get by index in collection push(model) ➠ Add to end of collection unshift(model) ➠ Add to start of collection pop() ➠ Remove and return last model shift() ➠ Remove and return first model length ➠ Count of models in collection
  • 64. Collection Events add ➠ Model added to Collection remove ➠ Model removed from Collection reset ➠ Collection's entire contents updated change ➠ Model's attributes have changed destroy ➠ Model is destroyed (deleted)
  • 65. QuizTableRowView QuizTableView
  • 66. QuizList QuizList = Collection.extend model: Quiz Server returns just _id, title, location, url: "/api/quizzes" created in descending order
  • 67. QuizTableView Alert visible when the QuizList is empty .alert strong. No Quizzes have been created yet ... feel free to start creating some! .limit-height table.table.table-bordered.table-striped.table-condensed thead tr th Title th.span3 Location th.span3 Created th.span3 tbody QuizTableRowView added as children .well button.btn.btn-primary.x-create-new Create New Quiz &nbsp; button.btn.x-create-test-data(data-loading-text="Loading ..."). Create Test Quizzes
  • 68. QuizTableView QuizTableView = View.extend initialize: -> @quizzes = new QuizList @$el.html readTemplate "QuizTableView" @$(".alert, table").hide() @quizzes.on "reset", @addAll, this @quizzes.on "add", @addOne, this @quizzes.on "destroy", @render, this @quizzes.fetch() @$(".x-create-test-data").popover title: "For Testing" content: "Creates many Quizzes with random text, for testing purposes. This will be removed in the final application." …
  • 69. QuizTableView.render() render: -> if @quizzes.length is 0 @$(".alert").show() @$("table").hide() else @$(".alert").hide() @$("table").show() this
  • 70. QuizTableView – Adding Models @quizzes.on "reset", @addAll, this @quizzes.on "add", @addOne, this addOne: (quiz, collection, options) -> view = new QuizTableRowView model: quiz collection: @quizzes # Special case for inserting at index 0, which happens when a new quiz # is added in the UI. fname = if options? and options.index is 0 then "prepend" else "append" @$("tbody")[fname] view.render().el addAll: (quizzes) -> @$("tbody").empty() quizzes.each (quiz) => @addOne quiz @render()
  • 71. QuizTableRowView view = new QuizTableRowView model: quiz collection: @quizzes QuizTableRowView = View.extend tagName: "tr" initialize: -> @model.on "change", @render, this @model.on "destroy", @remove, this @template = readTemplate "QuizTableRowView" render: -> @$el.html Mustache.render @template, title: @model.escape "title" or "<em>No Title</em>" location: @model.escape "location" created: @model.get "created" …
  • 73. Quiz enableSave() : boolean _id title location created 0..n rounds Round enableSave() : boolean kind title 0..n questions Question enableSave() : boolean text answer value
  • 74. ❝Backbone doesn't include direct support for nested models and collections or "has many" associations because there are a number of good patterns for modeling structured data on the client side, and Backbone should provide the foundation for implementing any of them.❞
  • 75. Quiz inside MongoDB { "title" : "NFJS", "location" : "San Antonio", "_id" : ObjectId("4f835669c34c2a9c6f000003"), "rounds" : [ # { "kind" : "normal", # "title" : "Server-Side Java", # "_id" : ObjectId("4f835669c34c2a9c6f000004"), # "questions" : [ ] } ], "created" : ISODate("2012-04-09T21:36:41.726Z") }
  • 76. Quiz.parse() Quiz = Model.extend idAttribute: "_id" urlRoot: "/api/quiz" parse: (response) -> response.rounds = _(response.rounds).map (raw) -> new Round raw, { parse: true } return response default: -> rounds: [] # of Round enableSave: -> (not isBlank @get "title") and _(@get "rounds").all (round) -> round.enableSave()
  • 77. Round.parse() Round = Model.extend default: -> questions: [] # of Question parse: (response) -> response.questions = _(response.questions).map (raw) -> new Question raw, { parse: true } return response enableSave: -> return false if isBlank @get "title" questions = @get "questions" _.all questions, (q) -> q.enableSave()
  • 78. Managing Relationships QuizRoundsEditorView = View.extend initialize: -> Build collection from simple array … @collection = new RoundCollection @model.get "rounds" @collection.on "all", => @model.set "rounds", @collection.toArray() @model.trigger "childChange" Update array from collection on any change RoundCollection = Collection.extend model: Round
  • 81. Not Covered Lots! Details of Sync Router ➠ encode state into URL Server-side details Lots of libraries on top of Backbone
  • 85. Q&A