SlideShare a Scribd company logo
1 of 85
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.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

Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Domenic Denicola
 
Singletons in PHP - Why they are bad and how you can eliminate them from your...
Singletons in PHP - Why they are bad and how you can eliminate them from your...Singletons in PHP - Why they are bad and how you can eliminate them from your...
Singletons in PHP - Why they are bad and how you can eliminate them from your...go_oh
 
GeeCON Prague 2014 - Metaprogramming with Groovy
GeeCON Prague 2014 - Metaprogramming with GroovyGeeCON Prague 2014 - Metaprogramming with Groovy
GeeCON Prague 2014 - Metaprogramming with GroovyIván López Martín
 
Source Code for Dpilot
Source Code for Dpilot Source Code for Dpilot
Source Code for Dpilot Nidhi Chauhan
 
Dpilot Source Code With ScreenShots
Dpilot Source Code With ScreenShots Dpilot Source Code With ScreenShots
Dpilot Source Code With ScreenShots DeepAnshu Sharma
 
Python client api
Python client apiPython client api
Python client apidreampuf
 
Design Patterns Reconsidered
Design Patterns ReconsideredDesign Patterns Reconsidered
Design Patterns ReconsideredAlex Miller
 
The state of your own hypertext preprocessor
The state of your own hypertext preprocessorThe state of your own hypertext preprocessor
The state of your own hypertext preprocessorAlessandro Nadalin
 
G*ワークショップ in 仙台 Grails(とことん)入門
G*ワークショップ in 仙台 Grails(とことん)入門G*ワークショップ in 仙台 Grails(とことん)入門
G*ワークショップ in 仙台 Grails(とことん)入門Tsuyoshi Yamamoto
 
Node.js in action
Node.js in actionNode.js in action
Node.js in actionSimon Su
 
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KZepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KThomas Fuchs
 
Xlab #1: Advantages of functional programming in Java 8
Xlab #1: Advantages of functional programming in Java 8Xlab #1: Advantages of functional programming in Java 8
Xlab #1: Advantages of functional programming in Java 8XSolve
 
Models and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsModels and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsRoss Tuck
 
Обзор фреймворка Twisted
Обзор фреймворка TwistedОбзор фреймворка Twisted
Обзор фреймворка TwistedMaxim Kulsha
 
Command Bus To Awesome Town
Command Bus To Awesome TownCommand Bus To Awesome Town
Command Bus To Awesome TownRoss Tuck
 
JavaScript - i och utanför webbläsaren (2010-03-03)
JavaScript - i och utanför webbläsaren (2010-03-03)JavaScript - i och utanför webbläsaren (2010-03-03)
JavaScript - i och utanför webbläsaren (2010-03-03)Anders Jönsson
 

What's hot (20)

Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
 
はじめてのGroovy
はじめてのGroovyはじめてのGroovy
はじめてのGroovy
 
Singletons in PHP - Why they are bad and how you can eliminate them from your...
Singletons in PHP - Why they are bad and how you can eliminate them from your...Singletons in PHP - Why they are bad and how you can eliminate them from your...
Singletons in PHP - Why they are bad and how you can eliminate them from your...
 
GeeCON Prague 2014 - Metaprogramming with Groovy
GeeCON Prague 2014 - Metaprogramming with GroovyGeeCON Prague 2014 - Metaprogramming with Groovy
GeeCON Prague 2014 - Metaprogramming with Groovy
 
Source Code for Dpilot
Source Code for Dpilot Source Code for Dpilot
Source Code for Dpilot
 
Dpilot Source Code With ScreenShots
Dpilot Source Code With ScreenShots Dpilot Source Code With ScreenShots
Dpilot Source Code With ScreenShots
 
Rails on Oracle 2011
Rails on Oracle 2011Rails on Oracle 2011
Rails on Oracle 2011
 
Python client api
Python client apiPython client api
Python client api
 
Say It With Javascript
Say It With JavascriptSay It With Javascript
Say It With Javascript
 
Design Patterns Reconsidered
Design Patterns ReconsideredDesign Patterns Reconsidered
Design Patterns Reconsidered
 
The state of your own hypertext preprocessor
The state of your own hypertext preprocessorThe state of your own hypertext preprocessor
The state of your own hypertext preprocessor
 
G*ワークショップ in 仙台 Grails(とことん)入門
G*ワークショップ in 仙台 Grails(とことん)入門G*ワークショップ in 仙台 Grails(とことん)入門
G*ワークショップ in 仙台 Grails(とことん)入門
 
Node.js in action
Node.js in actionNode.js in action
Node.js in action
 
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KZepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
 
Xlab #1: Advantages of functional programming in Java 8
Xlab #1: Advantages of functional programming in Java 8Xlab #1: Advantages of functional programming in Java 8
Xlab #1: Advantages of functional programming in Java 8
 
Models and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and HobgoblinsModels and Service Layers, Hemoglobin and Hobgoblins
Models and Service Layers, Hemoglobin and Hobgoblins
 
Обзор фреймворка Twisted
Обзор фреймворка TwistedОбзор фреймворка Twisted
Обзор фреймворка Twisted
 
Command Bus To Awesome Town
Command Bus To Awesome TownCommand Bus To Awesome Town
Command Bus To Awesome Town
 
ES6 Overview
ES6 OverviewES6 Overview
ES6 Overview
 
JavaScript - i och utanför webbläsaren (2010-03-03)
JavaScript - i och utanför webbläsaren (2010-03-03)JavaScript - i och utanför webbläsaren (2010-03-03)
JavaScript - i och utanför webbläsaren (2010-03-03)
 

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

Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Tsuyoshi Yamamoto
 
前端MVC 豆瓣说
前端MVC 豆瓣说前端MVC 豆瓣说
前端MVC 豆瓣说Ting Lv
 
Web Components With Rails
Web Components With RailsWeb Components With Rails
Web Components With RailsBoris Nadion
 
Rapid and Scalable Development with MongoDB, PyMongo, and Ming
Rapid and Scalable Development with MongoDB, PyMongo, and MingRapid and Scalable Development with MongoDB, PyMongo, and Ming
Rapid and Scalable Development with MongoDB, PyMongo, and MingRick Copeland
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejsNick Lee
 
Backbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVCBackbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVCpootsbook
 
The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196Mahmoud Samir Fayed
 
Secrets of JavaScript Libraries
Secrets of JavaScript LibrariesSecrets of JavaScript Libraries
Secrets of JavaScript Librariesjeresig
 
Overview of The Scala Based Lift Web Framework
Overview of The Scala Based Lift Web FrameworkOverview of The Scala Based Lift Web Framework
Overview of The Scala Based Lift Web FrameworkIndicThreads
 
Scala based Lift Framework
Scala based Lift FrameworkScala based Lift Framework
Scala based Lift Frameworkvhazrati
 
Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)Leonardo Soto
 
Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScriptAndrew Dupont
 
[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVCAlive Kuo
 
jQuery Rescue Adventure
jQuery Rescue AdventurejQuery Rescue Adventure
jQuery Rescue AdventureAllegient
 

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

Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
 
前端MVC 豆瓣说
前端MVC 豆瓣说前端MVC 豆瓣说
前端MVC 豆瓣说
 
Web Components With Rails
Web Components With RailsWeb Components With Rails
Web Components With Rails
 
Rapid and Scalable Development with MongoDB, PyMongo, and Ming
Rapid and Scalable Development with MongoDB, PyMongo, and MingRapid and Scalable Development with MongoDB, PyMongo, and Ming
Rapid and Scalable Development with MongoDB, PyMongo, and Ming
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejs
 
Backbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVCBackbone.js — Introduction to client-side JavaScript MVC
Backbone.js — Introduction to client-side JavaScript MVC
 
Green dao
Green daoGreen dao
Green dao
 
The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196
 
Secrets of JavaScript Libraries
Secrets of JavaScript LibrariesSecrets of JavaScript Libraries
Secrets of JavaScript Libraries
 
Overview Of Lift Framework
Overview Of Lift FrameworkOverview Of Lift Framework
Overview Of Lift Framework
 
Overview of The Scala Based Lift Web Framework
Overview of The Scala Based Lift Web FrameworkOverview of The Scala Based Lift Web Framework
Overview of The Scala Based Lift Web Framework
 
Scala based Lift Framework
Scala based Lift FrameworkScala based Lift Framework
Scala based Lift Framework
 
Backbone Basics with Examples
Backbone Basics with ExamplesBackbone Basics with Examples
Backbone Basics with Examples
 
Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)
 
Latinoware
LatinowareLatinoware
Latinoware
 
droidparts
droidpartsdroidparts
droidparts
 
Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScript
 
[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC
 
jQuery Rescue Adventure
jQuery Rescue AdventurejQuery Rescue Adventure
jQuery Rescue Adventure
 
DataMapper
DataMapperDataMapper
DataMapper
 

More from Howard Lewis Ship

Spock: A Highly Logical Way To Test
Spock: A Highly Logical Way To TestSpock: A Highly Logical Way To Test
Spock: A Highly Logical Way To TestHoward Lewis Ship
 
Modern Application Foundations: Underscore and Twitter Bootstrap
Modern Application Foundations: Underscore and Twitter BootstrapModern Application Foundations: Underscore and Twitter Bootstrap
Modern Application Foundations: Underscore and Twitter BootstrapHoward Lewis Ship
 
Have Your Cake and Eat It Too: Meta-Programming Techniques for Java
Have Your Cake and Eat It Too: Meta-Programming Techniques for JavaHave Your Cake and Eat It Too: Meta-Programming Techniques for Java
Have Your Cake and Eat It Too: Meta-Programming Techniques for JavaHoward Lewis Ship
 
Clojure: Towards The Essence Of Programming (What's Next? Conference, May 2011)
Clojure: Towards The Essence Of Programming (What's Next? Conference, May 2011)Clojure: Towards The Essence Of Programming (What's Next? Conference, May 2011)
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
Arduino: Open Source Hardware Hacking from the Software Nerd PerspectiveArduino: Open Source Hardware Hacking from the Software Nerd Perspective
Arduino: Open Source Hardware Hacking from the Software Nerd PerspectiveHoward Lewis Ship
 
Practical Clojure Programming
Practical Clojure ProgrammingPractical Clojure Programming
Practical Clojure ProgrammingHoward Lewis Ship
 
Clojure: Towards The Essence of Programming
Clojure: Towards The Essence of ProgrammingClojure: Towards The Essence of Programming
Clojure: Towards The Essence of ProgrammingHoward Lewis Ship
 
Tapestry 5: Java Power, Scripting Ease
Tapestry 5: Java Power, Scripting EaseTapestry 5: Java Power, Scripting Ease
Tapestry 5: Java Power, Scripting EaseHoward Lewis Ship
 
Brew up a Rich Web Application with Cappuccino
Brew up a Rich Web Application with CappuccinoBrew up a Rich Web Application with Cappuccino
Brew up a Rich Web Application with CappuccinoHoward Lewis Ship
 
Clojure: Functional Concurrency for the JVM (presented at OSCON)
Clojure: Functional Concurrency for the JVM (presented at OSCON)Clojure: Functional Concurrency for the JVM (presented at OSCON)
Clojure: Functional Concurrency for the JVM (presented at OSCON)Howard Lewis Ship
 
Tapestry: State of the Union
Tapestry: State of the UnionTapestry: State of the Union
Tapestry: State of the UnionHoward Lewis Ship
 
Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)
Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)
Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)Howard Lewis Ship
 

More from Howard Lewis Ship (16)

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

Recently uploaded

[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdfSandro Moreira
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsNanddeep Nachan
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAndrey Devyatkin
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherRemote DBA Services
 
Cyberprint. Dark Pink Apt Group [EN].pdf
Cyberprint. Dark Pink Apt Group [EN].pdfCyberprint. Dark Pink Apt Group [EN].pdf
Cyberprint. Dark Pink Apt Group [EN].pdfOverkill Security
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Victor Rentea
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024The Digital Insurer
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Orbitshub
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...apidays
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWERMadyBayot
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamUiPathCommunity
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024The Digital Insurer
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfOrbitshub
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024The Digital Insurer
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...DianaGray10
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FMESafe Software
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 

Recently uploaded (20)

[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Cyberprint. Dark Pink Apt Group [EN].pdf
Cyberprint. Dark Pink Apt Group [EN].pdfCyberprint. Dark Pink Apt Group [EN].pdf
Cyberprint. Dark Pink Apt Group [EN].pdf
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 

Backbone.js: Run your Application Inside The Browser

  • 1. Backbone.js: Run your Application Inside The Browser Howard M. Lewis Ship TWD Consulting hlship@gmail.com @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
  • 28.
  • 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