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

87
By Brian Sam-Bodden with jqm, spine.mvc, sinatra and mustache The Road to Mobile Web Development 1

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 of Road to mobile w/ Sinatra, jQuery Mobile, Spine.js and Mustache

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

By Brian Sam-Bodden

with jqm, spine.mvc, sinatra and mustache

The Road to Mobile Web Development

1

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

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

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

THE MOBILE WEBAGENT COMPUTING COMES OF AGE

3

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

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

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

• 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

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

“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

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

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

7

MOBILE WEBSEEN IN UNUSUAL PLACES

7

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

8

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

9

HTML5WHAT YOU NEED TO KNOW

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

9

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

10

HTML5WHAT YOU NEED TO KNOW

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

10

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

• 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

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

• 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

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

THE ROAD TO MOBILEBROWSER-BASED OR NATIVE?

13

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

WEB OR NATIVE?MAKING AN INFORM DECISION

14

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

14

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

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

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

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

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

• 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

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

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

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

DEVELOPMENT APPROACHWHERE TO START?

19

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

• 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

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

SINATRATHE LIGHTWEIGHT HTTP RUBY DSL

21

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

• 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

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

• 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

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

• 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

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

• Classy with logic less templates...

SINATRA W/ MUSTACHEA LIGHTWEIGHT SERVER SIDE ALTERNATIVE

25

25

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

• 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

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

• 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

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

JQUERY MOBILEA JQUERY-DRIVEN USER INTERFACE FRAMEWORK

28

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

• 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

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

• 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

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

SIMULATION, EMULATION & LIVE TESTING

WHAT TO DO WHEN

31

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

• 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

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

• 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

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

• 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

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

PAGESTHE BUILDING BLOCKS OF A MOBILE APPLICATION

35

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

• 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

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

• 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

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

• On the Android Emulator :

38

part_1/example_1.html

JQUERY MOBILEEMULATING A DEVICE

38

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

• On the iOS Emulator :

39

part_1/example_1.html

JQUERY MOBILEEMULATING A DEVICE

39

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

• On the iPhone 4:

40

part_1/example_1.html

JQUERY MOBILEEMULATING A DEVICE

40

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

• On the iPad

41

part_1/example_1.html

JQUERY MOBILEEMULATING A DEVICE

41

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

• 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

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

• 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

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

• 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

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

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

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

MULTI-PAGE DOCUMENTSINLINE PAGES

46

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

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

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

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

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

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

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

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

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

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

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

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

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

53

PAGE TRANSITIONSANIMATION BASED NAVIGATION

• The ‘flip’ transition animation in action:

part_1/example_2.html

53

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

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

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

55

BUTTONSSTYLIZED BUTTONS

• The newly added back button:

55

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

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

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

57

• The styled lists on the ‘other’ page:

LIST VIEWSSTYLING ORDERED OR UNORDERED LISTS

57

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

• 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

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

APPLICATION INITIALIZATION

USING THE JQUERY PLUGIN PATTERN

59

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

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

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

• 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

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

MVC IN JAVASCRIPTINTRODUCING SPINE.JS

62

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

SPINE.JSMVC FOR JAVASCRIPT

63

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

• 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

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

JQUERY SHOPPING CART

65

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

• 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

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

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

67

JQUERY SHOPPING CARTDRAG AND DROP SHOPPING CART

67

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

• jQuery Shopping Cart in action:

68

JQUERY SHOPPING CARTDRAG AND DROP SHOPPING CART

68

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

• 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

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

• 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

RESOURCES

86

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

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