Single-Page JavaScript Appsromania.amazon.com/academy/pdf/day2_1.pdfBackbone.js Library Overview...
Transcript of Single-Page JavaScript Appsromania.amazon.com/academy/pdf/day2_1.pdfBackbone.js Library Overview...
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 • Forums • Every web application
before AJAX
Single Page Applications • Gmail • 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?