The City Bars App with Sencha Touch 2
- 2. Sencha Touch
A JavaScript framework for building
rich mobile apps with web standards
- 4. Basically...
Get a WebKit-based desktop browser
Get some emulators & real devices
Download the Sencha Touch 2 PR2 SDK
Develop against a local web server
Optional, but highly recommended!
- 13. Pre-requisites
Yelp developer API key:
http://www.yelp.com/developers/
Install Sass and Compass:
http://sass-lang.com/download.html
http://compass-style.org/install/
- 15. Development sequence
1 App Architecture 5 List Event Handler
2 UI Structure 6 Detail Page
3 Data Modeling 7 Customize Theme
4 List Binding
- 19. index.html
<!doctype
html>
<html>
<head>
yay! HTML5
<title>City
Guide</title> JS + CSS*
<script
src="lib/touch2/sencha-‐touch-‐all-‐debug.js"></script>
<link
href="lib/touch2/resources/css/sencha-‐touch.css"
rel="stylesheet"
/>
<script
src="app/app.js"></script>
</head> our app
<body></body>
</html>
don’t panic
*or from CDN
- 20. app.js
global namespace
instantiates application
var
CB;
Ext.application({
launch:
function()
{
create main UI panel
CB
=
this;
CB.cards
=
Ext.create('Ext.Panel',
{
launch event
fullscreen:
true,
html:
'Hello
world'
});
}
config object
});
- 24. toolbar back toolbar
dataList
click
listCard detailCard
CB.cards
- 25. var
CB;
Ext.application({
launch:
function()
{
variable ref
CB
=
this;
CB.cards
=
Ext.create('Ext.Panel',
{
fullscreen:
true,
layout:
'card',
items:
[{ how children lay out
id:
'listCard',
html:
'List'
UI children
},
{
id:
'detailCard',
html:
'Detail' id-based ref
}]
});
}
});
- 29. Child component patterns I
var
list
=
new
Ext.List({
store:
store,
...
});
instantiate component
var
panel
=
new
Ext.Panel({
items:
[list,
...],
...
});
reference component by var
- 30. Child component patterns II
preferable in ST2
var
list
=
Ext.create('Ext.List',
{
store:
store,
...
});
var
panel
=
Ext.create('Ext.Panel',
{
items:
[list,
...]
...
});
- 31. Child component patterns III
var
panel
=
Ext.create('Ext.Panel',
{
items:
[
{
xtype:
'list',
store:
store,
...
},
... deferred creation*
],
...
});
* a lightweight object until then
- 33. The list card
{
//
the
list
card
id:
'listCard', list should fill whole card
layout:
'fit',
items:
[{
//
main
top
toolbar
docked top toolbar
xtype:
'toolbar',
docked:
'top',
title:
'Please
wait'
//
will
get
added
once
city
known
},
{
//
the
list
itself
list*
//
gets
bound
to
the
store
once
city
known
id:
'dataList',
xtype:
'list'
}]
}
* list will be bound to a store later
- 34. The detail card
{
//
the
details
card
id:
'detailCard',
items:
[{
//
detail
page
also
has
a
toolbar
docked
:
'top',
xtype:
'toolbar',
title:
'' another docked toolbar*
},
{
//
textual
detail
}]
}
detail page to come later...
* title will be dynamically set
- 37. The YELP API...
http://api.yelp.com/business_review_search
?ywsid=YELP_KEY
&term=BUSINESS_TYPE free, rate limited
&location=CITY
business type, and city name
- 40. "businesses":
[
{
"rating_img_url"
:
"http://media4.px.yelpcdn.com/...",
"country_code"
:
"US",
"id"
:
"BHpAlynD9dIGIaQDRqHCTA",
"is_closed"
:
false,
"city"
:
"Nashville",
"mobile_url"
:
"http://mobile.yelp.com/biz/...",
"review_count"
:
50,
"zip"
:
"11231",
"state"
:
"TN",
"latitude"
:
40.675758,
"address1"
:
"253
Conover
St",
"address2"
:
"",
"address3"
:
"",
"phone"
:
"7186258211",
"state_code"
:
"TN",
"categories":
[...], some fields are
...
},
...
useful for our app
]
- 41. Create a data model
give the ‘class’ a name
Ext.define("Business",
{
extend:
"Ext.data.Model",
fields:
[
extending base model
{name:
"id",
type:
"int"},
{name:
"name",
type:
"string"},
{name:
"latitude",
type:
"string"},
{name:
"longitude",
type:
"string"},
{name:
"address1",
type:
"string"},
{name:
"address2",
type:
"string"},
{name:
"address3",
type:
"string"},
{name:
"phone",
type:
"string"},
{name:
"state_code",
type:
"string"},
{name:
"mobile_url",
type:
"string"},
{name:
"rating_img_url_small",
type:
"string"},
{name:
"photo_url",
type:
"string"},
]
});
and with these named, typed fields
- 42. <aside>
Models can be associated
with other models
Fields can also have default values,
conversion functions, and validation
</aside>
- 43. Create a model store
create the store
var
store
=
Ext.create('Ext.data.Store',
{
model:
"Business",
...
});
containing this
type of model
Think of a store as a ‘table’ of model instance ‘rows’
- 44. Configure data source
loads as soon as possible
var
store
=
Ext.create('Ext.data.Store',
{
model:
'Business',
autoLoad:
true,
proxy:
{ JSONP
//
call
Yelp
to
get
business
data
type:
'scripttag',
source
url:
'http://api.yelp.com/business_review_search'
+
'?ywsid='
+
YELP_KEY
+
'&term='
+
escape(BUSINESS_TYPE)
+
'&location='
+
escape(DEFAULT_CITY)
,
reader:
{
type:
'json',
root:
'businesses' construct API URL
}
}
});
read array from inside JSON
- 45. Create constants
please change this!
<script>
YELP_KEY
=
'G3HueY_I5a8WZX-‐_bAAAA';
DEFAULT_CITY
=
'San
Francisco';
BUSINESS_TYPE
=
'Bars';
</script>
- 46. We can make the proxy URL dynamic,
which would allow geolocation.
But this requires an async
callback sequence.
- 47. Two-phase async sequence
getCity:
function
(callback)
{
callback(DEFAULT_CITY);
call when
//
this
could
now
be
a
geo
lookup
to
//
get
the
nearest
city
UI ready }, use this in the URL
getBusinesses:
function
(city,
callback)
{
Ext.define("Business",
{
...
});
the data code
we just wrote
var
store
=
Ext.create('Ext.data.Store',
{
...
});
} and this will need to fire the callback
with store when it autoloads
- 48. event
var
store
=
Ext.create('Ext.data.Store',
{ listeners
...
listeners:
{
//
when
the
records
load,
fire
the
callback
load:
function
(store)
{
callback(store);
when loaded
}
}
});
fire the callback with store
- 51. our 2 async functions
//
get
the
city
CB.getCity(function
(city)
{
//
then
use
Yelp
to
get
the
businesses
CB.getBusinesses(city,
function
(store)
{
//
then
bind
data
to
list
and
show
it
CB.cards.query('#dataList')[0].setStore(store);
});
});
get dataList bind the store to it
by its id
- 54. List items are templated
{
id:
'dataList',
xtype:
'list',
store:
null,
itemTpl:
'{name}'
}
model fields in curly braces
- 55. Spinner bound to store
instantiate mask over body
Ext.create('Ext.LoadMask',
Ext.getBody(),
{
store:
store,
msg:
''
});
will show when store is loading
- 57. A more interesting template
itemTpl:
'<img
class="photo"
src="{photo_url}"
width="40"
height="40"/>'
+
'{name}<br/>'
+
'<img
src="{rating_img_url_small}"/> '
+
'<small>{address1}</small>'
HTML allowed
- 58. Hack the style
<style>
.photo
{
float:left;
margin:0
8px
16px
0;
border:1px
solid
#ccc;
-‐webkit-‐box-‐shadow:
0
2px
4px
#777;
}
</style>
- 62. { when list items
selection
id:
'dataList',
...
are selected
listeners:
{
selectionchange:
function
(selectionModel,
records)
{
//
if
selection
made,
slide
to
detail
card
if
(records[0])
{
CB.cards.setActiveItem(1);
also fires on detail card
deselection
CB.cards.getActiveItem().setData(
records[0].data
);
...to detail
}
} apply record data... page template
}
}
- 63. A back button
items:
[{ children of toolbars
//
detail
page
also
has
a
toolbar are implicitly
docked
:
'top',
xtype:
'toolbar', xtype: ‘button’
title:
'',
items:
[{
//
containing
a
back
button
arrow style
//
that
slides
back
to
list
card
text:
'Back',
ui:
'back', back to list
listeners:
{
tap:
function
()
{
CB.cards.setActiveItem(0);
when tapped
}
}
}],
...
- 66. style this card as
regular HTML
{
//
textual
detail
styleHtmlContent:
true,
cls:
'detail', CSS class for styling
tpl:
[
'<img
class="photo"
src="{photo_url}"
/>',
'<h2>{name}</h2>',
template for
'<div
class="info">',
'{address1}<br/>',
a whole panel
'<img
src="{rating_img_url_small}"/>',
'</div>',
'<div
class="phone
x-‐button">',
'<a
href="tel:{phone}">{phone}</a>',
'</div>',
'<div
class="link
x-‐button">',
'<a
href="{mobile_url}">Read
more</a>',
'</div>'
]
}]
- 69. Override setData
set title on toolbar
setData:
function
(data)
{
this.query('toolbar')[0].setTitle(data.name);
this.query('[cls="detail"]')[0].setData(data);
},
apply data to template on inner panel
- 71. A little styling
.x-‐html
h2
{
margin-‐bottom:0;
}
.phone,
.link
{
clear:both; formatting
font-‐weight:bold; the buttons
display:block;
text-‐align:center;
margin-‐top:8px;
}
temporary .detail
{
fixes
-‐webkit-‐box-‐orient:
vertical;
}
.detail
.photo
{
float:none;
}
- 73. One final tweak
move from {
inner panel...
//
textual
detail
cls:
'detail',
styleHtmlContent:
true,
...
{
//
the
details
card ...to outer card
id:
'detailCard',
styleHtmlContent:
true,
- 77. Development sequence
1 App Architecture 5 List Event Handler
2 UI Structure 6 Detail Page
3 Data Modeling 7 Customize Theme
4 List Binding
- 80. Packaging
Add to home screen
- Icon
- Splash screen
Hybrid app; PhoneGap / NimbleKit
- Contacts API
- Geolocation
http://sencha.com/x/cy
http://sencha.com/x/de
- 83. O ine data
Taking Yelp data o ine
Taking images o ine
- src.sencha.io to generate cross-origin B64
Detecting network connection changes
http://sencha.com/x/df