Road to mobile w/ Sinatra, jQuery Mobile, Spine.js and Mustache

Post on 01-Nov-2014

5,830 views 3 download

description

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.

transcript

By Brian Sam-Bodden

with jqm, spine.mvc, sinatra and mustache

The Road to Mobile Web Development

1

WHAT I HAVE FOR YOU...

2

• 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

THE MOBILE WEBAGENT COMPUTING COMES OF AGE

3

“...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

MOBILE WEBAGENT COMPUTING COMES OF AGE

4

• 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)

MOBILE WEBSOME STATISTICS

5

5

“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

MOBILE WEBMOVE OVER PCS

6

• In a Mall Kiosk in Ft. Lauderdale, Florida:

7

MOBILE WEBSEEN IN UNUSUAL PLACES

7

8

9

HTML5WHAT YOU NEED TO KNOW

Big Picture: Canvas, Video, Geolocation and Offline Web Apps

9

10

HTML5WHAT YOU NEED TO KNOW

Big Picture: IE Still Sucks! Who owns a windows phone anyways? :-)

10

• 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

HTML5WHAT YOU NEED TO KNOW

11

11

• 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

HTML5WHAT YOU NEED TO KNOW

12

12

THE ROAD TO MOBILEBROWSER-BASED OR NATIVE?

13

WEB OR NATIVE?MAKING AN INFORM DECISION

14

• Number of Mobile Web Apps have quickly surpassed their native counterparts

14

WEB OR NATIVE?MAKING AN INFORM DECISION

15

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

WEB OR NATIVE?MAKING AN INFORM DECISION

16

Factor Native Web

# of target devices

More Devices == More Projects, Inconsistencies between products

One codebase (with small tweaks) to rule 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

multimedia Audio/Video/ 2D and 3D graphics more accessible from native APIs

HTML 5 Video / Canvas coming along, WebGL future uncertain(1)

device integrationTouch Gestures / Accelerometer / Gyroscope / Camera / Geo-

location / File System / System Alerts more/only accessible from native APIs

Hacks abound to make some of these work

audiencegamers, selective downloaders (only download if app provides significant advantages of others) and the “only if I can have an

icon for it” crowd

anybody with a browser that it is inclined to use it!

16

• If your application core relies on 3D, File System, Camera, Mobile Security, System Alerts, Marketplace, Performance go Native

• If your 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

WEB OR NATIVE?MAKING AN INFORM DECISION

17

17

WEB OR NATIVE?MAKING AN INFORM DECISION

18

... and there is always the hybrid approach!

build it for the Web, convert it to Native

http://rhomobile.com/https://webmynd.com/http://phonegap.com/http://appcelerator.com/

18

DEVELOPMENT APPROACHWHERE TO START?

19

• 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 :-)

APPROACHWHERE TO START?

20

20

SINATRATHE LIGHTWEIGHT HTTP RUBY DSL

21

• Why?

• Rails might be too heavy for our purposes

• You will/might need to support native clients

• API-driven and JSON is the payload

SINATRAA LIGHTWEIGHT SERVER SIDE ALTERNATIVE

22

22

• Multiple mini-apps in one process...

SINATRAA LIGHTWEIGHT SERVER SIDE ALTERNATIVE

23

map '/' do run Comida::ComidaWebAppend

map '/api' do run Comida::ComidaApiend

module Comida # # The Web App # class ComidaWebApp < Sinatra::Base ... end # # The API # class ComidaApi < Sinatra::Base ... endend

23

• 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!

MUSTACHELOGIC-LESS TEMPLATES

24

24

• Classy with logic less templates...

SINATRA W/ MUSTACHEA LIGHTWEIGHT SERVER SIDE ALTERNATIVE

25

25

• Render a mustache template (with a layout too).

26

class ComidaWebApp < Sinatra::Base register Mustache::Sinatra require './views/layout'

get '/' do mustache :search end

{{> search_page}}

{{> search_results_page}}

<script type="text/javascript" src="javascripts/search.js"></script>

template/search.mustache

<section data-role="page" id="search_results"> <header data-role="header"> <h1>Resturants</h1> <nav data-role="navbar"> <ul> <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 -->

SINATRA W/ MUSTACHEA LIGHTWEIGHT SERVER SIDE ALTERNATIVE

template/search_result_page.mustache

26

• Simple API with Sinatra...

SINATRAA LIGHTWEIGHT SERVER SIDE ALTERNATIVE

27

get '/search.json' do response = {} response[:restaurants] = restaurants content_type :json response.to_jsonend

• Let’s explore the sample’s app API with IRB and CURL

27

JQUERY MOBILEA JQUERY-DRIVEN USER INTERFACE FRAMEWORK

28

• A lightweight markup-driven User Interface (UI) framework for mobile web applications

• Build on 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

JQUERY MOBILEINTRODUCTION

29

• To start 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

JQUERY MOBILESETTING YOUR DEVELOPMENT ENVIRONMENT

30

SIMULATION, EMULATION & LIVE TESTING

WHAT TO DO WHEN

31

• 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)

• The simplest way to test is to run a local Web Server on your development machine and access the application over a WIFI network

32

JQUERY MOBILEEMULATION CHOICES

32

• The Android SDK (http://developer.android.com) includes a device emulator that can support many Android Virtual Devices (AVDs).

33

JQUERY MOBILETHE ANDROID EMULATOR

33

• The iOS Simulator (http://developer.apple.com/programs/ios/) is tucked away in Apple’s free iPhone Software Development Kit

34

JQUERY MOBILETHE ANDROID EMULATOR

The iOS simulator can be found under /Developer/Platforms/iPhoneSimulator.platform/Developer/Applications and it is aptly named “iOS Simulator”

34

PAGESTHE BUILDING BLOCKS OF A MOBILE APPLICATION

35

• Let’s open the example_1.html page in Safari (5.1.2 shown below) at 1024x768:

36

part_1/example_1.html

jQuery Mobile applications can also be

used on desktop, laptops and tablets

JQUERY MOBILEEMULATING A DEVICE

36

• In Safari (5.1.2) at 640x960 (iPhone 4 resolution):

37

part_1/example_1.html

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

JQUERY MOBILEEMULATING A DEVICE

37

• On the Android Emulator :

38

part_1/example_1.html

JQUERY MOBILEEMULATING A DEVICE

38

• On the iOS Emulator :

39

part_1/example_1.html

JQUERY MOBILEEMULATING A DEVICE

39

• On the iPhone 4:

40

part_1/example_1.html

JQUERY MOBILEEMULATING A DEVICE

40

• On the iPad

41

part_1/example_1.html

JQUERY MOBILEEMULATING A DEVICE

41

• 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 the course

42

part_1/example_1.html

JQUERY MOBILEEMULATING A DEVICE

42

• The HTML document source is shown below:

43

JQUERY MOBILEGETTING STARTED

<!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 -->

</body></html>

part_1/example_1.html

43

• The body of the document consists of several div elements

• The div are decorated with HTML5 data-role attributes

• The outer div has a role of page and contains two stacked inner divs with the roles header and content

44

JQUERY MOBILEGETTING STARTED

<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 -->

</body></html>

part_1/example_1.html

44

45

part_1/example_1.html

JQUERY MOBILEEMULATING A DEVICE

<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css" />

• The link tag applies the jQuery Mobile CSS stylesheet to the document

• Commenting out the CSS reveals the simplicity of the underlying markup

45

MULTI-PAGE DOCUMENTSINLINE PAGES

46

47

MULTI-PAGE DOCSA 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 -->

</div><!-- /page -->part_1/example_2.html

47

48

MULTI-PAGE DOCSLOCATION HASH BASED NAVIGATION

http://.../part_1/example_2.html http://.../part_1/example_2.html#other

• 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)

48

49

MULTI-PAGE DOCSLINKING 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> <p>Check out this <a href="#other">other</a> page</p> </div><!-- /content --> <div data-role="footer" data-position="fixed"> <h4>The Footer</h4> </div><!-- /footer -->

</div><!-- /page -->

• Let’s fill add navigation links to the other page from the home page and vice-versa:

part_1/example_2.html

<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">back!</a></p> </div><!-- /content --> <div data-role="footer" data-position="fixed"> <h4>The Footer</h4> </div><!-- /footer -->

</div><!-- /page -->

49

50

MULTI-PAGE DOCSBACK LINKS

• A generic 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:

part_1/example_2.html

<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 -->

50

51

MULTI-PAGE DOCSA DOCUMENT WITH A COUPLE OF PAGES

• We can now navigate from #home to #other and back:

part_1/example_2.html

51

52

PAGE TRANSITIONSANIMATION BASED NAVIGATION

<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 -->

• So far 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

part_1/example_2.html

52

53

PAGE TRANSITIONSANIMATION BASED NAVIGATION

• The ‘flip’ transition animation in action:

part_1/example_2.html

53

54

BUTTONSSTYLIZED BUTTONS

<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

• 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

54

55

BUTTONSSTYLIZED BUTTONS

• The newly added back button:

55

56

LIST VIEWSSTYLING ORDERED OR UNORDERED LISTS

<div data-role="page" id="other"> ... <div data-role="content"> <h3>Unordered List</h3> <ul data-role="listview"> <li>Apples</li> <li>Oranges</li> <li>Peaches</li> </ul> <h3>Ordered List</h3> <ol data-role="listview"> <li>Woke up</li> <li>Fell out of bed</li> <li>Dragged a comb across my head</li> </ol> </div><!-- /content --> ...</div><!-- /page -->

part_1/example_2.html

• jQuery Mobile provides a “listview” data-role attribute that can turn an ordered or unordered list into a nicely styled native looking list

• Let’s modify the “other” page to show a couple of lists

56

57

• The styled lists on the ‘other’ page:

LIST VIEWSSTYLING ORDERED OR UNORDERED LISTS

57

• 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

DEMO 1.0LAUNCHING THE RESTAURANT MOBILE SITE

58

APPLICATION INITIALIZATION

USING THE JQUERY PLUGIN PATTERN

59

60

INITIALIZATIONUSING THE JQUERY PLUGIN PATTERN WITH JQUERY MOBILE

(function($) { var methods = { initPageOne : function(options) { }, initPageTwo : function(options) { }, initAllPages : function(options) { $().initApp("initPageOne"); $().initApp("initPageTwo"); } } $.fn.initApp = function(method) { // Method calling logic if ( methods[method] ) { return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 )); } else if ( typeof method === 'object' || ! method ) { return methods.initAllPages.apply( this, arguments ); } else { $.error( 'Method ' + method + ' does not exist' ); } }})(jQuery);

60

• Finally we can initialize the application by calling the initApp function from within a document ready handler function:

61

INITIALIZATIONUSING THE JQUERY PLUGIN PATTERN WITH JQUERY MOBILE

<script>$(document).ready(function() { $().initApp();})</script>

61

MVC IN JAVASCRIPTINTRODUCING SPINE.JS

62

SPINE.JSMVC FOR JAVASCRIPT

63

• Spine.js is a small library provides a micro-MVC framework for your JavaScript applications

• It allow 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

SPINE.JSMVC FOR JAVASCRIPT

64

JQUERY SHOPPING CART

65

• 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

JQUERY SHOPPING CARTDRAG AND DROP SHOPPING CART

66

• The jQuery powered Shopping Cart project structure is shown below:

67

JQUERY SHOPPING CARTDRAG AND DROP SHOPPING CART

67

• jQuery Shopping Cart in action:

68

JQUERY SHOPPING CARTDRAG AND DROP SHOPPING CART

68

• 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

JQUERY SHOPPING CARTDRAG AND DROP SHOPPING CART

69

• 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

JQUERY SHOPPING CARTDRAG AND DROP SHOPPING CART

70

SPINE.JSMVC 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

SPINE.JSMVC FOR JAVASCRIPT

• Models: In Spine, models are created using the setup method of Spine.Model, which takes the name of the model and an array of properties

72

// 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

SPINE.JSMVC FOR JAVASCRIPT

• To make the model persists between page reloads we extend the Item with the Spine.Model.Local module

73

// Persist model between page reloads.Item.extend(Spine.Model.Local);

part_2/examples/jquery-spine-shopping-cart/app/models/item.js

• The extend 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

SPINE.JSMVC FOR JAVASCRIPT

• To add behavior to our model we use the include method which adds instance properties

• The four methods that we need for our Item model; increase, decrease, total and label

74

// Instance methodsItem.include({ // total: function() { return (this.price * this.quantity); }, // increase: function(quantity) { quantity = (typeof(quantity) != 'undefined') ? quantity : 1; this.quantity = this.quantity + quantity; this.save(); }, // decrease: function(quantity) { quantity = (typeof(quantity) != 'undefined') ? quantity : 1; if (this.quantity >= quantity) { this.quantity = this.quantity - quantity; } else { this.quantity = 0; } this.save(); }, // label: function() { return (this.name + " - $" + this.price); } });

part_2/examples/jquery-spine-shopping-cart/app/models/item.js

74

SPINE.JSMVC 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.

75

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

SPINE.JSMVC FOR JAVASCRIPT

• Controllers: Controllers in 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

SPINE.JSMVC 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

77

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

SPINE.JSMVC FOR JAVASCRIPT

• ... cont.

78

render: function(){ this.el = $.mustache($("#cartItem").html(), this.item); return this; }, // event handlers add: function(e) { this.item.increase(); }, remove: function(e) { this.item.decrease(); }, // ui methods updateQty: function() { $('#item_' + this.item.pid + ' #qty') .text(this.item.quantity) .effect("highlight", {}, 1500); } });})

part_2/examples/jquery-spine-shopping-cart/app/controllers/cart_item.js

78

SPINE.JSMVC 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.

79

<!-- 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

SPINE.JSMVC FOR JAVASCRIPT

• To test 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

80

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

SPINE.JSMVC FOR JAVASCRIPT

• The ShoppingCart controller which will manage a collection of Item models. Internally the ShoppingCart keeps the items dropped in the items property

• The clear, total, isEmpty and itemsCount methods fulfill the business functionality of the cart

81

jQuery(function($){ window.ShoppingCart = Spine.Controller.sub({ el: $("#theCart"), init: function() { var cart = this; this.items = {}; $.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(); }); }, // removes all items from the cart clear: function() { $.each(this.items, function(){ this.destroy(); }); this.items = {}; this.updateCartTotal(); }, total: function() { var sum = 0.0; $.each(this.items, function(){ sum += this.total(); });

return sum; }, isEmpty: function() { return this.itemsCount() == 0; }, itemsCount: function() { var size = 0; var items = this.items; $.each(items, function(){ if (items.hasOwnProperty(this)) size++; });

return size; }, ...

part_2/examples/jquery-spine-shopping-cart/app/controllers/shopping_cart.js

81

SPINE.JSMVC FOR JAVASCRIPT

82

...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

• 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

82

SPINE.JSMVC FOR JAVASCRIPT

83

...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

• 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

83

SPINE.JSMVC FOR JAVASCRIPT

• The removeItem, updateCartTotal, removeIfQuantityZero and addItem deal with responding to UI events and updating the UI

84

...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

SPINE.JSMVC FOR JAVASCRIPT

85

part_4/examples/jquery-spine-shopping-cart/app/application.js

• 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();});

85

RESOURCES

86

RESOURCES• Sinatra: http://www.sinatrarb.com/

• jQuery Mobile: http://jquerymobile.com

• Spine.js: http://spinejs.com

• Example Code:

• https://github.com/bsbodden/jquery-shopping-cart

• https://github.com/bsbodden/jquery-spine-shopping-cart

• https://github.com/integrallis/jqm-spine-sinatra-demo

87

87