Ruby is powerful server-side language with great collection of libraries and frameworks but to create a full mobile offering, Ruby developers need to become masters of many a craft. In this talk we'll walk through the design and development of a full stack HTML5 mobile application using Sinatra to create a robust RESTful API, Spine.js to bring MVC order to the client and jQuery Mobile to style and structure the application for the mobile world.
Road to mobile w/ Sinatra, jQuery Mobile, Spine.js and Mustache
1. The Road to
Mobile Web Development
with jqm, spine.mvc, sinatra and mustache
By Brian Sam-Bodden
1
2. WHAT I HAVE FOR YOU...
•A possible path to mobile web applications:
• Sinatra + Mustache for a Simpler Server-Side
• jQuery Mobile for Out-of-the-box Mobile
• Spine.js for MVC goodness on the Client-Side
• Where to start? Front-to-Back or Back-to-Front?
2
2
4. MOBILE WEB
AGENT COMPUTING COMES OF AGE
“...the future of computing is
mobile... businesses should
have their best developers
working on their mobile
applications”
Google’s Eric Schmidt Interview on TechCrunch, April 2010
4
4
5. MOBILE WEB
SOME STATISTICS
• 20%: decline of home PC usage since 2008 (1)
• 74%: increase of smart phones sold from 2010 to 2011(2)
• 261%: increase in smart tablets sold from 2010 to 2011(3)
• $9 billion: predicted US mobile shopping sales in 2011 (4)
• 10.8%: people who used a mobile device to visit a retailer’s
site on Cyber Monday (up from 3.9% in 2010) (4)
5
5
6. MOBILE WEB
MOVE OVER PCS
“Manufacturers shipped more
smartphones than personal
computers in the fourth
quarter of 2010... crowning
mobile devices as the
computing platform of choice.
Financial Times Article, February 2011
6
6
7. MOBILE WEB
SEEN IN UNUSUAL PLACES
• In a Mall Kiosk in Ft. Lauderdale, Florida:
7
7
9. HTML5
WHAT YOU NEED TO KNOW
Big Picture: Canvas, Video, Geolocation and Offline Web Apps
9
9
10. HTML5
WHAT YOU NEED TO KNOW
Big Picture: IE Still Sucks! Who owns a windows phone anyways? :-)
10
10
11. HTML5
WHAT YOU NEED TO KNOW
• Simplified header and doctype: <!DOCTYPE html>
• Script tag doesn’t need type attribute
• Many more semantic tags added like header, nav, section,
article, aside, footer, details, summary, address, figure,
output, time, pubdate, nav, first, last, next, prev, menu,
command, mark, strong, progress, meter and many, many
more
• Data attributes (data-*)
• Media Tags
11
11
12. HTML5
WHAT YOU NEED TO KNOW
• Native GeoLocation API for Mobile Browsers
• 2D Graphics with Canvas
• Many new form input types
• Datalist
• Markup-driven form validation
• Local Storage, Web Sockets, Web Workers
12
12
13. THE ROAD TO MOBILE
BROWSER-BASED OR NATIVE?
13
14. WEB OR NATIVE?
MAKING AN INFORM DECISION
• Number of Mobile Web Apps have quickly surpassed their
native counterparts
14
14
15. WEB OR NATIVE?
MAKING AN INFORM DECISION
Taptu predicts that "the
Mobile Touch Web will grow...
and will approach the quality ..
of [Native] Apps across all the
app categories except for
games."
From “Mobile App or Browser-Based Site? Report Says The Browser Will Win on Mobile”
February 2010, ReadWriteWeb.com
15
15
16. WEB OR NATIVE?
MAKING AN INFORM DECISION
Factor Native Web
# of target More Devices == More Projects, Inconsistencies between One codebase (with small tweaks) to rule
devices products them all
distribution Application Stores Processes/Charge Upfront :-) Instant
time to market Slow! Faster for simpler applications
developer skills Specialized UI developer skills: iOS, Android, etc. HTML/JavaScript/CSS + Frameworks
Audio/Video/ 2D and 3D graphics more accessible from native HTML 5 Video / Canvas coming along,
multimedia APIs WebGL future uncertain(1)
Touch Gestures / Accelerometer / Gyroscope / Camera / Geo-
device integration location / File System / System Alerts more/only accessible Hacks abound to make some of these work
from native APIs
gamers, selective downloaders (only download if app provides
anybody with a browser that it is inclined to
audience significant advantages of others) and the “only if I can have an
use it!
icon for it” crowd
16
16
17. WEB OR NATIVE?
MAKING AN INFORM DECISION
• If
your application core relies on 3D, File System, Camera,
Mobile Security, System Alerts, Marketplace, Performance go
Native
• Ifyour application success hinges on ease of deployment and
distribution, deeply linked pages, social media and constant
updates/changes then go Web
• Factors that are improving and/or are close to match their
native counterparts include audio, video, 2D graphics, input
and gestures, accelerometer and gyroscope, geolocation and
native-like application launcher
17
17
18. WEB OR NATIVE?
MAKING AN INFORM DECISION
... and there is always the
hybrid approach!
http://appcelerator.com/ http://phonegap.com/ https://webmynd.com/ http://rhomobile.com/
build it for the Web, convert it
to Native
18
18
20. APPROACH
WHERE TO START?
• Server-side developers gravitate to fleshing out the API first
• Client-side developers start with the look and feel
• After
trying both I’ve decided that it is best to start at both
ends and meet in the middle :-)
20
20
22. SINATRA
A LIGHTWEIGHT SERVER SIDE ALTERNATIVE
• Why?
• Rails might be too heavy for our purposes
• You will/might need to support native clients
• API-driven and JSON is the payload
22
22
23. SINATRA
A LIGHTWEIGHT SERVER SIDE ALTERNATIVE
• Multiple mini-apps in one process...
module Comida
#
# The Web App
map '/' do #
run Comida::ComidaWebApp class ComidaWebApp < Sinatra::Base
end ...
end
map '/api' do
run Comida::ComidaApi #
end # The API
#
class ComidaApi < Sinatra::Base
...
end
end
23
23
24. MUSTACHE
LOGIC-LESS TEMPLATES
•I wanted simple templates
that I could render on the
client and on the server
• No Ruby in my views, no
markup in my Ruby!
24
24
25. SINATRA W/ MUSTACHE
A LIGHTWEIGHT SERVER SIDE ALTERNATIVE
• Classy with logic less templates...
25
25
26. SINATRA W/ MUSTACHE
A LIGHTWEIGHT SERVER SIDE ALTERNATIVE
• Render a mustache template (with a layout too).
{{> search_page}}
{{> search_results_page}}
<script type="text/javascript" src="javascripts/search.js"></script>
template/search.mustache
class ComidaWebApp < Sinatra::Base
register Mustache::Sinatra
<section data-role="page" id="search_results">
require './views/layout'
<header data-role="header">
<h1>Resturants</h1>
get '/' do <nav data-role="navbar">
mustache :search <ul>
end <li><a href="#home" class="ui-btn-active">Search</a></li>
<li><a href="#menu">Menus</a></li>
<li><a href="#order">Order</a></li>
</ul>
</nav>
</header>
<div data-role="content" id="content_main">
<form action="/api/menus.json" method="get" data-ajax="false"
id="restaurant_selection_form">
<div id="restaurants_found"></div>
<button type="submit" data-theme="a">Submit</button>
</form>
</div>
</section><!-- /page -->
template/search_result_page.mustache
26
26
27. SINATRA
A LIGHTWEIGHT SERVER SIDE ALTERNATIVE
• Simple API with Sinatra...
get '/search.json' do
response = {}
response[:restaurants] = restaurants
content_type :json
response.to_json
end
• Let’s explore the sample’s app API with IRB and CURL
27
27
29. JQUERY MOBILE
INTRODUCTION
•Alightweight markup-driven User Interface (UI) framework for
mobile web applications
• Buildon JavaScript and making extensive use of the jQuery
JavaScript Library (about 12K minified)
• Promotes the use of clean, semantic HTML that gets enhanced
progressively and degrades gracefully if needed
• Supports a large variety of hardware and device features
• Supports Accessible Rich Internet Applications (WAI-ARIA)
• Theme-able following the ThemeRoller philosophy of jQuery UI
29
29
30. JQUERY MOBILE
SETTING YOUR DEVELOPMENT ENVIRONMENT
• Tostart on your path to web development for mobile devices
you’ll need:
• Editor: An editor capable of dealing with HTML, CSS and
JavaScript and potentially with your server-side language/
platform/framework of choice
• Emulation/Simulation: A way to emulate the different
devices and resolutions that your application/website wants
to target
• Debugging: For more complex applications the ability to
debug, trace and analyze JavaScript code
30
30
32. JQUERY MOBILE
EMULATION CHOICES
• The Mobile Web Development cycle involves coding,
previewing often on a regular desktop/laptop web browser
and interspersed testing on a particular device emulator or on
the target device itself* (particularly to test the feel of any gesture based interaction, device orientation changes and
other features that can only be experienced on the device itself)
• Thesimplest way to test is to run a local Web Server on your
development machine and access the application over a WIFI
network
32
32
33. JQUERY MOBILE
THE ANDROID EMULATOR
• The Android SDK (http://developer.android.com) includes a
device emulator that can support many Android Virtual
Devices (AVDs).
33
33
34. JQUERY MOBILE
THE ANDROID EMULATOR
• The iOS Simulator (http://developer.apple.com/programs/ios/)
is tucked away in Apple’s free iPhone Software Development
Kit
The iOS simulator can be found under
/Developer/Platforms/iPhoneSimulator.platform/Developer/Applications
and it is aptly named “iOS Simulator”
34
34
36. JQUERY MOBILE
EMULATING A DEVICE
• Let’s
open the example_1.html page in Safari (5.1.2 shown
below) at 1024x768:
jQuery Mobile
applications can also be
used on desktop,
laptops and tablets
part_1/example_1.html
36
36
37. JQUERY MOBILE
EMULATING A DEVICE
• In Safari (5.1.2) at 640x960 (iPhone 4 resolution):
The iPhone, iPad
and iPods use a
mobile version of
Safari so desktop
Safari at iPhone/
iPad resolutions is a
perfect browser for
rapid development
part_1/example_1.html
37
37
38. JQUERY MOBILE
EMULATING A DEVICE
• On the Android Emulator:
part_1/example_1.html
38
38
39. JQUERY MOBILE
EMULATING A DEVICE
• On the iOS Emulator:
part_1/example_1.html
39
39
40. JQUERY MOBILE
EMULATING A DEVICE
• On the iPhone 4:
part_1/example_1.html
40
40
41. JQUERY MOBILE
EMULATING A DEVICE
• On the iPad
part_1/example_1.html
41
41
42. JQUERY MOBILE
EMULATING A DEVICE
• The basic document contains a
single Page with a Title and a
Body showing some simple
content
• jQuery Mobile Documents can
contain one or more mobile
pages as well explore later in
part_1/example_1.html the course
42
42
43. JQUERY MOBILE
GETTING STARTED
• The HTML document source is shown below:
<!DOCTYPE html>
<html>
<head>
<title>Browser Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script type="text/javascript" src="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js"></script>
</head>
<body>
<div data-role="page">
<div data-role="header">
<h1>Page Title</h1>
</div><!-- /header -->
<div data-role="content">
<p>This is the content</p>
</div><!-- /content -->
</div><!-- /page --> part_1/example_1.html
</body>
</html>
43
43
44. JQUERY MOBILE
GETTING STARTED
<body>
<div data-role="page">
• The body of the document
<div data-role="header">
consists of several div elements
<h1>Page Title</h1>
</div><!-- /header -->
• The div
are decorated with
<div data-role="content">
<p>This is the content</p>
HTML5 data-role attributes
</div><!-- /content -->
• The outer div has a role of page
</div><!-- /page -->
and contains two stacked inner
</body>
</html> divs with the roles header and
content
part_1/example_1.html
44
44
45. JQUERY MOBILE
EMULATING A DEVICE
• The link tag applies the jQuery Mobile CSS stylesheet to the
document
• Commenting out the CSS reveals the simplicity of the
underlying markup
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css" />
part_1/example_1.html
45
45
47. MULTI-PAGE DOCS
A DOCUMENT WITH A COUPLE OF PAGES
<div data-role="page" id="home">
<div data-role="header">
<h1>Home</h1>
</div><!-- /header -->
<div data-role="content">
<p>This is the home page</p>
</div><!-- /content -->
<div data-role="footer" data-position="fixed">
<h4>The Footer</h4>
</div><!-- /footer -->
</div><!-- /page -->
<div data-role="page" id="other">
<div data-role="header">
<h1>Other</h1>
</div><!-- /header -->
<div data-role="content">
<p>This is another page</p>
</div><!-- /content -->
<div data-role="footer" data-position="fixed">
<h4>The Footer</h4>
</div><!-- /footer -->
part_1/example_2.html
</div><!-- /page -->
47
47
48. MULTI-PAGE DOCS
LOCATION HASH BASED NAVIGATION
• HTML5 provides the window.location.hash which is exploited
by jQuery Mobile to provide navigation to individual pages within
a multi-page document
• Adding #other to the
URL will navigate to the
“other” page using the
default transition (slide)
http://.../part_1/example_2.html http://.../part_1/example_2.html#other
48
48
49. MULTI-PAGE DOCS
LINKING PAGES
• Let’s
fill add navigation links to the other page from the home
page and vice-versa:
<div data-role="page" id="home"> <div data-role="page" id="other">
<div data-role="header"> <div data-role="header">
<h1>Home</h1> <h1>Other</h1>
</div><!-- /header --> </div><!-- /header -->
<div data-role="content"> <div data-role="content">
<p>This is the home page</p> <p>This is another page</p>
<p>Check out this <a href="#other">other</a> page</p> <p>Take me <a href="#home">back!</a></p>
</div><!-- /content --> </div><!-- /content -->
<div data-role="footer" data-position="fixed"> <div data-role="footer" data-position="fixed">
<h4>The Footer</h4> <h4>The Footer</h4>
</div><!-- /footer --> </div><!-- /footer -->
</div><!-- /page --> </div><!-- /page -->
part_1/example_2.html
49
49
50. MULTI-PAGE DOCS
BACK LINKS
•Ageneric way to create a link to the previous page (without
adding a new entry to the browser history) is to use the data-
rel= “back” attribute as shown below:
<div data-role="page" id="other">
<div data-role="header">
<h1>Other</h1>
</div><!-- /header -->
<div data-role="content">
<p>This is another page</p>
<p>Take me <a href="#home" data-rel="back">back!</a></p>
</div><!-- /content -->
<div data-role="footer" data-position="fixed">
<h4>The Footer</h4>
</div><!-- /footer -->
</div><!-- /page -->
part_1/example_2.html
50
50
51. MULTI-PAGE DOCS
A DOCUMENT WITH A COUPLE OF PAGES
• We can now navigate from #home to #other and back:
part_1/example_2.html
51
51
52. PAGE TRANSITIONS
ANIMATION BASED NAVIGATION
• Sofar we have seen the default ‘slide’ transition when navigating
from page to page
• jQuery Mobile provides several transitions that we can apply
via the data-transition attribute
• Let’s
modify the link to the “other” page to use the “flip”
transition animation
<div data-role="content">
<p>This is the home page</p>
<p>Check out this <a href="#other" data-transition="flip">other</a> page</p>
</div><!-- /content -->
part_1/example_2.html
52
52
53. PAGE TRANSITIONS
ANIMATION BASED NAVIGATION
• The ‘flip’ transition animation in action:
part_1/example_2.html
53
53
54. BUTTONS
STYLIZED BUTTONS
• jQuery Mobile provides a “button” data-role attribute that can
be applied to a link to turn it into a nicely styled button
• Let’s add a back button link to our “other” page
<div data-role="page" id="other">
<div data-role="header">
<a href="#home" data-role="button" data-rel="back" data-icon="back" >Back</a>
<h1>Other</h1>
</div><!-- /header -->
part_1/example_2.html
54
54
55. BUTTONS
STYLIZED BUTTONS
• The newly added back button:
55
55
56. LIST VIEWS
STYLING ORDERED OR UNORDERED LISTS
• jQuery Mobile provides a <div data-role="page" id="other">
...
“listview” data-role <div data-role="content">
<h3>Unordered List</h3>
attribute that can turn an <ul data-role="listview">
<li>Apples</li>
ordered or unordered list <li>Oranges</li>
<li>Peaches</li>
into a nicely styled native </ul>
<h3>Ordered List</h3>
looking list <ol data-role="listview">
<li>Woke up</li>
<li>Fell out of bed</li>
• Let’smodify the “other” <li>Dragged a comb across my head</li>
</ol>
page to show a couple of </div><!-- /content -->
...
lists </div><!-- /page -->
part_1/example_2.html
56
56
57. LIST VIEWS
STYLING ORDERED OR UNORDERED LISTS
• The styled lists on the ‘other’ page:
57
57
58. DEMO 1.0
LAUNCHING THE RESTAURANT MOBILE SITE
• The beginnings of a mobile website for a restaurant
• On the home page we want to display the restaurant’s name and
a logo alongside some welcome text and a button link to a
“menu” page
• On the menu page we will display a list of menu items and a back
button to the home page
58
58
61. INITIALIZATION
USING THE JQUERY PLUGIN PATTERN WITH JQUERY MOBILE
• Finally
we can initialize the application by calling the initApp
function from within a document ready handler function:
<script>
$(document).ready(function() {
$().initApp();
})
</script>
61
61
64. SPINE.JS
MVC FOR JAVASCRIPT
• Spine.jsis a small library provides a micro-MVC framework
for your JavaScript applications
• Itallow you to create a separation between the model
(business JavaScript code) and the view (which renders the
DOM)
• When models change their associated views are re-rendered
without tightly coupling them to the DOM
• Spine.js
keeps state/model in a single space, model changes
propagate automatically to the views with very little glue
code
64
64
66. JQUERY SHOPPING CART
DRAG AND DROP SHOPPING CART
• Let’s walk through the code of a jQuery powered shopping cart from the
ground up
• The markup consists of two lists styled to appear side-by-side and an
element showing the total amount of the items in the shopping cart
• Each retail item will be represented by a div containing an image (drag
handle), encode the item price in the div
• As items are dragged into the cart, use an effect to alert the user of the
cart total amount changes
• The non-visual JS code uses the prototype pattern to separate the UI
interactions from the business interactions
• As items are dropped into the cart, add controls to the items to
increase the quantity
66
66
67. JQUERY SHOPPING CART
DRAG AND DROP SHOPPING CART
• The jQuery powered Shopping Cart project structure is shown below:
67
67
68. JQUERY SHOPPING CART
DRAG AND DROP SHOPPING CART
• jQuery Shopping Cart in action:
68
68
69. JQUERY SHOPPING CART
DRAG AND DROP SHOPPING CART
• At first glance the implementation of the shopping cart seems to work as
advertised and even seems well thought- out. That is, until we start asking
the hard questions.
• Shortcoming of the jQuery Shopping Cart:
• From the usability point of view, the first thing that jumps to mind is,
what happens when the user clicks the refresh button?
• Since everything is just being kept in memory the answer is that it
simply goes away
69
69
70. JQUERY SHOPPING CART
DRAG AND DROP SHOPPING CART
• Shortcoming of the jQuery Shopping Cart (cont.):
• For anything more complex we would end up with an unmanageable
mess of UI callbacks all of which are currently living in the global
JavaScript scope.
• Another problem glaring problem is the coupling of the concepts of the
shopping cart and the items in the shopping cart.
• The items are simple JavaScript objects with no behavior, their reflection
on the UI is controlled by the decorateForCart function
70
70
71. SPINE.JS
MVC FOR JAVASCRIPT
• Spine.js
is partly based on Backbone’s API yet Spine’s take on
the MVC pattern is slightly different
•I used Spine.js to refactor the jQuery Shopping Cart
• Problems I wanted to fix on the shopping cart solution:
• Using the back button or the refresh button loses the carts
contents
• UI callbacks and manual event handling and triggering can
quickly get out of hand
71
71
72. SPINE.JS
MVC FOR JAVASCRIPT
• Models: InSpine, models are created using the setup method
of Spine.Model, which takes the name of the model and an
array of properties
// Create the Item model.
var Item = Spine.Model.sub();
Item.configure("Item", "name", "pid", "price", "quantity");
part_2/examples/jquery-spine-shopping-cart/app/models/item.js
72
72
73. SPINE.JS
MVC FOR JAVASCRIPT
• Tomake the model persists between page reloads we
extend the Item with the Spine.Model.Local module
// Persist model between page reloads.
Item.extend(Spine.Model.Local);
part_2/examples/jquery-spine-shopping-cart/app/models/item.js
• Theextend method adds class properties to the model. We
now have an object that can be created, saved and retrieved
from the browser local storage.
73
73
74. SPINE.JS
MVC FOR JAVASCRIPT
// Instance methods
Item.include({
//
• To add behavior to our
total: function() {
return (this.price * this.quantity); model we use the
},
// include method which
adds instance properties
increase: function(quantity) {
quantity = (typeof(quantity) != 'undefined') ? quantity : 1;
this.quantity = this.quantity + quantity;
this.save();
},
// • The four methods that
we need for our Item
decrease: function(quantity) {
quantity = (typeof(quantity) != 'undefined') ? quantity : 1;
if (this.quantity >= quantity) {
}
this.quantity = this.quantity - quantity;
model; increase,
else {
this.quantity = 0; decrease, total and
label
}
this.save();
},
//
label: function() {
return (this.name + " - $" + this.price);
}
});
part_2/examples/jquery-spine-shopping-cart/app/models/item.js
74
74
75. SPINE.JS
MVC FOR JAVASCRIPT
• To instantiate an Item we use the create method which
takes an object literal for the parameters. A new model can
be persisted using the save method. When a model is saved
it is assigned an identifier that can be retrieved via the id
property.
var item = new Item({name: "Product 1", pid: "0001" , price: 100.0, quantity: 1});
item.save();
alert("Just saved Item with id => " + item.id);
75
75
76. SPINE.JS
MVC FOR JAVASCRIPT
• Controllers: Controllersin Spine are a combination of a
traditional MVC controller and a view. Therefore controllers
are in charge of rendering and manipulating one or more
models in the context of controller’s functionality
• Our first Spine controller will deal with the rendering and
manipulation of an individual Item. The CartItem controller
will deal with user interface events to increase and decrease
the quantity of a Item while keeping the user abreast of the
changes.
76
76
77. SPINE.JS
MVC FOR JAVASCRIPT
• Spine controllers like the CartItem are created using the
create method of Spine.Controller, which takes an object
literal that a wide variety of properties
jQuery(function($){
window.CartItem = Spine.Controller.sub({
init: function(){
var cartItem = this;
this.item.bind("quantityChanged", function() { cartItem.updateQty() });
$('#item_' + this.item.pid + ' .add').live('click', function(e) {
cartItem.add();
e.preventDefault();
});
$('#item_' + this.item.pid + ' .remove').live('click', function(e) {
cartItem.remove();
e.preventDefault();
});
},
part_2/examples/jquery-spine-shopping-cart/app/controllers/cart_item.js
77
77
79. SPINE.JS
MVC FOR JAVASCRIPT
• The cartItem template is a Mustache.js Template that enables
the creation of markup templates containing binding
expressions. The mustache method clones the template contents
and replaces the binding expressions ({{exp}}) with the values
of the object passed, in our case the controller’s enclosed Item
model.
<!-- Mustache :-{)~ Template for CartItem -->
<script type="text/x-mustache-tmpl" id="cartItem">
<li class="product ui-state-default" id="item_{{pid}}" price="{{price}}">
{{label}}
(<span id="qty">{{quantity}}</span>)
<a href="#" class="add">+</a>
<a href="#" class="remove">-</a>
</li>
</script>
part_2/examples/jquery-spine-shopping-cart/shopping_cart.html
79
79
80. SPINE.JS
MVC FOR JAVASCRIPT
• Totest our controller we need to instantiate an Item model and
use it to instantiate the controller. We can then render the
controller on the DOM of an HTML page
var item = new Item({name: "Product 1", pid: "0001" , price: 100.0, quantity: 1});
var view = new CartItem({item: item});
$("#item").html(view.render().el);
part_2/examples/jquery-spine-shopping-cart/tests/cart_item_test.html
80
80
81. SPINE.JS
MVC FOR JAVASCRIPT
jQuery(function($){
• The ShoppingCart
window.ShoppingCart = Spine.Controller.sub({
el: $("#theCart"),
controller which will manage
init: function() {
var cart = this;
this.items = {};
a collection of Item models.
$.each(Item.all(), function(){ cart.addItem(this); });
this.el.droppable({ accept: '.product', drop: this.proxy(this.drop) });
$('#dump', this.el).live('click', function() { cart.clear(); });
Internally the
},
// removes all items from the cart
clear: function() {
$.each(this.items, function(){ this.destroy(); });
this.items = {};
this.updateCartTotal();
ShoppingCart keeps the
},
total: function() {
items dropped in the items
var sum = 0.0;
$.each(this.items, function(){ sum += this.total(); }); property
• The clear, total, isEmpty
return sum;
},
isEmpty: function() {
},
return this.itemsCount() == 0;
and itemsCount methods
itemsCount: function() {
var size = 0;
var items = this.items;
fulfill the business
$.each(items, function(){ if (items.hasOwnProperty(this)) size++; });
return size;
functionality of the cart
},
...
part_2/examples/jquery-spine-shopping-cart/app/controllers/shopping_cart.js
81
81
82. SPINE.JS
MVC FOR JAVASCRIPT
• The drop method handles drop events; it either creates a new
item or increases the quantity of an existing item. It creates an
Item model that is then rendered using the CartItem controller
...
drop: function(ev, ui) {
var item_dropped = ui.draggable;
var pid = item_dropped.attr('id');
var price = item_dropped.attr('price');
var name = item_dropped.attr('name');
if (this.items.hasOwnProperty(pid)) {
this.items[pid].increase();
}
else {
var item = Item.create({name: name, pid: pid, price: price, quantity: 1});
this.addItem(item);
$(".items").append(CartItem.init({item: item}).render().el);
}
},
part_2/examples/jquery-spine-shopping-cart/app/controllers/shopping_cart.js
82
82
83. SPINE.JS
MVC FOR JAVASCRIPT
• The render method render the #shoppingCart template,
decorates the #dump button and loops through the contained
Items creating a CartItem for each and rendering them in
the .items
...
render: function() {
this.el.html($.mustache($("#shoppingCart").html(), {}));
$('#dump').button();
$.each(this.items, function(){
$(".items").append(CartItem.init({item: this}).render().el);
});
this.updateCartTotal();
},
part_2/examples/jquery-spine-shopping-cart/app/controllers/shopping_cart.js
83
83
84. SPINE.JS
MVC FOR JAVASCRIPT
• The removeItem, updateCartTotal, removeIfQuantityZero
and addItem deal with responding to UI events and updating
the UI ...
removeItem: function(item) {
$('#item_' + item.pid).effect("puff", {}, "slow", function(){ $(this).remove(); });
},
updateCartTotal: function() {
$('#total').text(this.total()).effect("highlight", {}, 1500);
},
removeIfQuantityZero: function(item) {
if (item.quantity == 0) {
this.removeItem(item);
delete this.items[item.pid];
item.destroy();
}
},
addItem: function(item) {
this.items[item.pid] = item;
item.bind("quantityChanged", this.proxy(this.updateCartTotal));
item.bind("quantityChanged", this.proxy(this.removeIfQuantityZero));
item.bind("quantityChanged", function() { item.save() });
item.bind("destroy", this.proxy(this.removeItem));
item.save();
this.updateCartTotal();
}
part_2/examples/jquery-spine-shopping-cart/app/controllers/shopping_cart.js
84
84
85. SPINE.JS
MVC FOR JAVASCRIPT
• Finally
to kick everything in motion the file application.js file
begins by fetching any previously stored Item records, making
the sample products draggable, creating a cart and rendering it.
jQuery(function($){
Item.fetch();
$(".product").draggable({ helper: 'clone', opacity: "0.5" });
var cart = new ShoppingCart();
cart.render();
});
part_4/examples/jquery-spine-shopping-cart/app/application.js
85
85