Single-Page JavaScript Apps - Amazon Development Center Romania

bolivialodgeInternet and Web Development

Dec 14, 2013 (3 years and 9 months ago)

102 views

Single
-
Page JavaScript Apps

with
RequireJS

and Backbone.js

Mihai
Bîrsan

Who is this guy?

Mihai
Bîrsan

Sr. Web Development Engineer

Email Tools Team

Amazon Development Center Romania




We’ve recently rebuilt our project’s UI using
Backbone.js and Require.js (among others)


Single
-
Page Applications explained


Example application


MVC and Backbone.js


AMD and Require.js

What are Single
-
Page Applications?


Recent new way to develop

web applications

What are classic web applications?


Request


Page response


Page
-
by
-
page


The web was invented as a collection of
static documents


What are Single
-
Page Applications?


JavaScript client application


Pulls data with AJAX


Has an internal state that’s not
necessarily represented on the server

Examples in the wild

Classic web

applications


F潲畭o


Every

web application
before AJAX

Single

Page Applications


䝭慩G


Amazon Search Page

How do we develop
TodoMVC


Think about the architecture of the app


Separate concerns


data: the actual to
-
do items


presentation: displaying data


behavior: creating and filtering
data

How do we develop
TodoMVC

Model

Controller

View

data

behavior

presentation

MVC and Single Page Applications


Built around user
input



Events


No single point of control



No Controller

How do we develop
TodoMVC

Model

Events

View

data

behavior

presentation

Scaffolding: Basic structure


index.html



obviously, the container


styles/*.
css



the presentation


javascripts
/*.
js



the behavior


javascripts
/main.js



app entry point

Backbone.js Library Overview


Models & Collections


Views


Events


Router

Backbone Model


A special type of object


Keeps track of changed attributes


Fires events when its internal state
changes

var

TodoModel
=

Backbone.Model.extend({



// Default attributes for the todo



// and ensure that each todo created



// has `title` and `completed` keys.



defaults: {



title:
''
,



completed:
false



},




// Toggle the `completed` state of this todo item.



toggle
:
function

() {



this
.save({



completed:
!
this
.get(
'completed'
)



});



}

});

Todo

Model

Model usage example

var

todo
=

new

TodoModel
({ text:
"Do the dishes."

});


// Later: Update the text

todo.set(
"text"
,
"Do the dishes NOW!"
);

todo.save();


// Even later: Mark the task as complete

todo.toggle();

Todos

Collection

var

TodosCollection
=

Backbone.Collection.extend({



// Reference to this collection's model.



model: Todo,




// Filter down the list: all finished todo items.



completed
:
function

() {



return

this
.filter(
function

(
todo
) {



return

todo.get(
'completed'
);



});



},




// Filter down the list: all incomplete todo items.



remaining
:
function

() {



return

this
.without.
apply
(
this
,
this
.completed());



}

});

Collection usage example

var

todos
=

new

TodosCollection
([



{ text:
"Get milk"

},



{ text:
"Make cake"

},



{ text:
"Eat the cake"

}

]);


// Mark the first item as completed

todos.at(
0
).toggle();


// Check number of incomplete items

console
.log
(todos.remaining().
length
);

Displaying information


Application View


displays the whole collection


uses
Todo

views to display each model


Todo

View


displays a single
Todo

model

Todo

View

var

TodoView
=

Backbone.View.extend({



tagName:


'li'
,




//
This is a function that generates HTML



template: _.template(todosTemplate),




render
:
function

() {



this
.
$
el.html(
this
.template(
this
.model.toJSON()));



this
.
$
el.toggleClass(


'completed'
,


this
.model.get(
'completed'
)


);




this
.toggleVisible();



this
.
$
input
=

this
.
$
(
'.edit'
);



return

this
;



},

Tying it up with events


View events trigger changes in the model


click the check mark



toggle the completeness


edit the input



update the text

Todo

View’s own events

var

TodoView

=

Backbone.View.extend
({


events: {



'click .toggle'
:
'
toggleCompleted
'
,



'
dblclick

label'
:
'edit'
,



'click .destroy'
:
'clear'
,



'
keypress

.edit'
:
'
updateOnEnter
'
,



'blur .edit'
:
'close'



},




// Toggle the `"completed"` state of the model.



toggleCompleted
:
function

() {



this
.model.toggle
();



},

some lines hidden

Tying it up with events


Model events trigger updates in the view


when any code
makes a
Todo

invisible



the view hides it


when any attribute of the model changes



the view re
-
renders

Todo

View’s model events

var

TodoView
=

Backbone.View.extend({



initialize
:
function

() {



this
.listenTo(
this
.model,
'change'
,
this
.render);



this
.listenTo(
this
.model,
'destroy'
,
this
.remove);



this
.listenTo(


this
.model,
// the object to listen to


'visible'
,
// the event name


this
.toggleVisible
// the function to call


);



},




toggleVisible
:
function

() {



this
.
$
el.toggleClass(
'hidden'
,


this
.isHidden());



},

};

some lines hidden

some lines hidden

More interesting code


app.js



the main application view


router.js



Backbone Router to
respond to URL changes and update it


templates/*.html



partial HTML
documents, transformed with
_.template()

Interconnected modules

App View

Todo

View

Todo

Model


Todos

Collection


Todo

Model

Interconnected modules, actually

App View

Todo

View

template
todos.html

Todo

Model

template
stats.html

Todos

Collection

Todo

Model

jQuery

Backbone

Underscore

Utilities

Require.js Library Overview


Define dependencies


Asynchronously load dependencies

Dependencies in
app.js

define([


'jquery'
,


'underscore'
,


'backbone'
,


'collections/todos'
,


'views/todos'
,


'text!templates/stats.html'
,


'common'

],
function

(
$, _, Backbone, Todos, TodoView,


statsTmpl, Common
) {

some lines missing

Prettier
define

call, IMHO

define(
function

(
require
) {



var

$

=

require(
'jquery'
),



_

=

require(
'underscore'
),



Backbone

=

require(
'backbone'
),



Todos

=

require(
'collections/todos'
),



TodoView

=

require(
'views/todos'
),



statsTmpl

=

require(
'text!templates/stats.html'
),



Common

=

require(
'common'
);

some lines missing

How definition works


Each file contains one module


Modules are defined by calling
define


The callback passed to
define

returns
the actual module object

The definition of
Todo

Model

define([



'underscore'
,



'backbone'

],
function

(
_, Backbone
) {



'use strict'
;




var

TodoModel
=

Backbone.Model.extend({





});




return

TodoModel;

});

some lines hidden

How dependencies are loaded


The string passed to
define

or
require

is the path of the JavaScript file


If require has already loaded the file

the associated module is returned


Else the module is being loaded


The callback is called once all modules
have been loaded


The
require

function


In the simplified version of
define

call,

it is magically transformed



require("...")

is never called!


Will always return immediately


the module, if already loaded


null, if not loaded; also starts loading


Can be passed a callback

The
require

function

// In an arbitrary script (not in define)

var

_
=

require(
'underscore'
);

// _ is null, because require hasn't loaded Underscore yet



// Much later, after Underscore has been loaded

var

_
=

require(
'underscore'
);

// _ is the expected Underscore object



// Altogether, if necessary

require([
'underscore'
],
function

(
_
) {



// Underscore is loaded when this function is called

});

What about saving the data?


localStorage

is used for this example


after the break we talk about a back
-
end
solution for storing data, working out of
the box with Backbone

When not to use this


Not all problems have to be solved with
the MVW pattern


Choose the pattern that best suits the
situation


One
-
off scripts and hacks don’t need this


Hack it today!


http://todomvc.com
/





http://
backbonejs.org/


http://requirejs.org/

fork it on

Thank you!

Questions?