Upload
ron-reiter
View
17.757
Download
0
Embed Size (px)
DESCRIPTION
A walkthrough of how to write a complete HTML5 web app (both front end and back end) using Google App Engine (Python), Backbone.js, Require.js, underscore.js and jQuery.
Citation preview
Writing HTML5 Web Apps
Google App Engine
Backbone.js
Require.js
jQuery
Ron Reiter © 2012
Agenda Why do you need to learn how to program HTML5 web
apps?
A walkthrough over the To-Do list anatomy https://github.com/ronreiter/webapp-boilerplate
Why? Web Apps = Software as a Service
Cross OS/platform/browser Cuts costs on deployment and maintenance Scales easily
Google App Engine, Amazon, Heroku, etc.
Mobile Apps Cross device development cuts development costs
WebView PhoneGap
Brief Introduction Google App Engine - Solid, scalable server framework
Platform as a Service
Backbone.js - Proven MVC framework LinkedIn mobile, Foursquare, Do.com, Groupon, Posterous,
Basecamp mobile, Kicksend, etc...
Require.js - Modular JavaScript Loader Module dependency management JavaScript minification & bundling
jQuery - DOM Manipulation Framework Creating dynamic content and replacing Flash
The To-Do List
Web App Architecture
Front EndBackbone.js
Back EndGoogle App Engine
REST API
Back-End
Dataset We want to create a Todo list item table.
Start by adding a Google App Engine model
# the Todo model.
class Todo(db.Model):
content = db.StringProperty()
done = db.BooleanProperty()
order = db.IntegerProperty()
Request Handler Serves all web requests
We implement a main handler and REST API
def main():
application = webapp.WSGIApplication([
# index.html
('/', MainHandler),
# REST interface
('/todos', TodoListHandler),
('/todos/(\d+)', TodoItemHandler),
], debug=True)
util.run_wsgi_app(application)
Main Request Handler When we access http://www.todolist.com, the app is
downloaded to our computer and starts to run.
class MainHandler(webapp.RequestHandler):
def get(self):
self.response.out.write(
template.render("index.html", {}))
REST API Standard REST API is used to let clients control datasets, using
four CRUD operations: Create a new item – using HTTP POST requests Read a list of items – using HTTP GET requests Update a single item – using HTTP PUT requests Delete a single item – using HTTP DELETE requests
Similar to SQL, but this is a customized interface and not a way to access a database
REST API uses XML or JSON serialization (Javascript Object Notation) to encode and decode objects
We’ll use JSON
REST API – GET (get all tasks)
class TodoListHandler (webapp.RequestHandler):
def get(self):
# serialize all Todos,
# include the ID in the response
todos = []
for todo in Todo.all():
todos.append({
"id" : todo.key().id(),
"content" : todo.content,
"done" : todo.done,
"order" : todo.order,
})
# send them to the client as JSON
self.response.out.write(simplejson.dumps(todos))
REST API – POST (add a new task)class TodoListHandler (webapp.RequestHandler):
def post(self):
data = simplejson.loads(self.request.body) # load JSON data of the object
todo = Todo(
content = data["content"],
done = data["done"],
order = data["order"],
).put() # create the todo item
# send it back, and include the new ID.
self.response.out.write(simplejson.dumps({
"id" : todo.id(),
"content" : data["content"],
"done" : data["done"],
"order" : data["order"],
}))
REST API – PUT (update a task)
class TodoItemHandler (webapp.RequestHandler):
def put(self, id):
data = simplejson.loads(self.request.body) # load the updated model
todo = Todo.get_by_id(int(id)) # get it model using the ID from the request path
todo.content = data["content"]
todo.done = data["done"]
todo.order = data["order"]
todo.put() # update all fields and save to the DB
# send it back using the updated values
self.response.out.write(simplejson.dumps({
"id" : id,
"content" : todo.content,
"done" : todo.done,
"order" : todo.order,
}))
REST API – DELETE (delete a task)class TodoItemHandler (webapp.RequestHandler):
def delete(self, id):
# find the requested model and delete it.
todo = Todo.get_by_id(int(id))
todo.delete()
Front-End
Backbone.js Architecture – MVC
ViewHTML + CSS
ControllerBackbone.View
ModelBackbone.Model
DOM ManipulationWith jQuery andtemplating
View Events
Backbone REST Sync
Server Model Database
Model Events
Backbone.js Architecture – REST Sync
ModelView
ModelView
ModelView
ModelView
Collection
PUT /tasks/41DELETE /tasks/41
PUT /tasks/39DELETE /tasks/39
PUT /tasks/40DELETE /tasks/40
PUT /tasks/38DELETE /tasks/38
GET /tasksPOST /tasks
Collection OperationsModel Operations
Web App Directory Structure index.html – Main entry point
css todos.css – Defines the style
js libs
require.js, jQuery, Backbone, Underscore models
todo.js – The todo item model collections
todos.js – The todo item collection views
todos.js – The todo item view App.js – The app view
templates stats.html todo.html – The todo item template
main.js – Require.js entry point
index.html Loads the stylesheet
<link
rel="stylesheet"
href="css/todos.css”/>
Loads the main.js script
<script
data-main="js/main"
src="js/libs/require/require.js">
</script>
main.js Configures paths and known libraries
text is used for require.js text loading (for templates)
require.config({
paths: {
jquery: 'libs/jquery/jquery-min',
underscore: 'libs/underscore/underscore-min',
backbone: 'libs/backbone/backbone-optamd3-min',
text: 'libs/require/text'
}
});
main.js – cont. Load the app view (views/app.js)
Notice there is no need to add the JS extension
Require.js supplies us with a function to run Javascript code only after certain modules have been loaded.
This allows us to create a dependency model, and a new way to write Javascript modules.
require(['views/app'], function(AppView){
var app_view = new AppView;
});
views/app.js – The AppView Backbone's "View" is actually a "View Controller". The
view itself is the template combined with CSS.
Creating a new view either means creating a new DOM element using JavaScript/jQuery or
templating Using an existing one in the DOM
Since there is only one app, we’ll use an existing element for the AppView.
However, task views are more dynamic, and they will create new DOM elements.
views/app.js – The AppView (cont.)
The define function allows us to depend on libraries, template files and other modules we wrote.
Every dependency in the list corresponds to an argument of the function to execute once all modules are loaded.
define([
'jquery’, 'underscore', 'backbone',
'collections/todos’, 'views/todos',
'text!templates/stats.html'
], function($, _, Backbone,
Todos, TodoView, statsTemplate) {
var AppView = Backbone.View.extend({ ...
views/app.js – The AppView (cont.)
The "View" captures and delegates events on DOM elements
events: {
"keypress #new-todo": "createOnEnter",
"click .todo-clear a": "clearCompleted”
},
views/app.js – The AppView (cont.)
We add some handlers on the Todos collection to get notified when it's updated.
Then, we fetch the Todos collection from the server.
Once the data is loaded, addAll will be executed.
initialize: function() {
Todos.bind('add', this.addOne);
Todos.bind('reset', this.addAll);
Todos.fetch();
},
collections/todos.js – Todo Collection
Backbone Collections are model arrays which synchronize with the server.
The AppView listens for changes on this collection, and can add new models to it.
define([
'underscore', 'backbone', 'models/todo’
], function(_, Backbone, Todo){
var TodosCollection = Backbone.Collection.extend({
model: Todo,
url: '/todos',
done: function() {
return this.filter(function(todo){
return todo.get('done');
});
}
});
models/todo.js – Todo Model Holds the Todo item data
Defines the default values and methods related to the data
The "define" function needs to return the model class. When "model/todo" will be required, it will be received.
define(['underscore', 'backbone'], function(_, Backbone) {
var TodoModel = Backbone.Model.extend({
defaults: { content: "empty todo...”, done: false, order: 0 }
});
return TodoModel;
});
views/todos.js – Todo View Bind the view to the model
Render function uses the model data to render the template
define(['jquery', 'underscore', 'backbone’, 'models/todo',
'text!templates/todos.html'
], function($, _, Backbone, Todo, todosTemplate){
var TodoView = Backbone.View.extend({
model: Todo,
template: _.template(todosTemplate),
initialize: function() {
this.model.bind('change', this.render);
this.model.bind('destroy', this.remove);
},
render: function() {
$(this.el).html(this.template(this.model.toJSON()));...
views/todos.js – Todo View (cont.) Manipulate the view's model according to triggered events
events: {
"click .check" : "toggleDone",
...
}
// Toggle the "done" state of the model.
toggleDone: function() {
this.model.save({done : !this.model.get("done")});
},
templates/todos.html Template files are used to build the views either:
Once (and then update using jQuery) On every update (if you're lazy)
Use <%- ... -> for escaping HTML
<div class="todo <%= done ? 'done' : '' %>">
<div class="display">
<input class="check"
type="checkbox" <%= done ? 'checked="checked"' : '' %> />
<div class="todo-content"><%- content %></div>
<span class="todo-destroy"></span>
</div>
<div class="edit">
<input class="todo-input" type="text" value="<%- content %>" />
</div>
</div>
Questions?