SlideShare a Scribd company logo
The City Bars App
       with

Sencha Touch 2
Sencha Touch

 A JavaScript framework for building
rich mobile apps with web standards
http://sencha.com/x/d5
http://sencha.com/x/ed
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!
The City Bars App with Sencha Touch 2
The City Bars App with Sencha Touch 2
http://sencha.com/touch
stylesheet




script framework
Introducing the
 City Bars App
The City Bars App with Sencha Touch 2
The City Bars App with Sencha Touch 2
http://sencha.com/x/ee

http://senchalearn.github.com/citybars2
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/
http://github.com/
senchalearn/citybars2
Development sequence
1 App Architecture   5 List Event Handler

2 UI Structure       6 Detail Page

3 Data Modeling      7 Customize Theme

4 List Binding
The City Bars App with Sencha Touch 2
Application Architecture

1_app_architecture
application
entry point



JS + CSS in SDK
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
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
               });
The City Bars App with Sencha Touch 2
The City Bars App with Sencha Touch 2
UI Structure

2_ui_structure
toolbar        back      toolbar
dataList

           click

listCard              detailCard
           CB.cards
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
            	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }]
            	
  	
  	
  	
  	
  	
  	
  	
  });
            	
  	
  	
  	
  }

                     });
The City Bars App with Sencha Touch 2
<aside>
 about layouts
and components
Layouts
card             vbox




fit              hbox
Child component patterns I

     var	
  list	
  =	
  new	
  Ext.List({
     	
  	
  	
  	
  store:	
  store,
     	
  	
  	
  	
  ...
     });
                                        instantiate component
     var	
  panel	
  =	
  new	
  Ext.Panel({
     	
  	
  	
  	
  items:	
  [list,	
  ...],
     	
  	
  	
  	
  ...
     });

                             reference component by var
Child component patterns II
                                       preferable in ST2

      var	
  list	
  =	
  Ext.create('Ext.List',	
  {
      	
  	
  	
  	
  store:	
  store,
      	
  	
  	
  	
  ...
      });


      var	
  panel	
  =	
  Ext.create('Ext.Panel',	
  {
      	
  	
  	
  	
  items:	
  [list,	
  ...]
      	
  	
  	
  	
  ...
      });
Child component patterns III

      var	
  panel	
  =	
  Ext.create('Ext.Panel',	
  {
      	
  	
  	
  	
  items:	
  [
      	
  	
  	
  	
  	
  	
  	
  	
  {
      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  xtype:	
  'list',
      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  store:	
  store,
      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  ...
      	
  	
  	
  	
  	
  	
  	
  	
  },	
  ...                         deferred   creation*
      	
  	
  	
  	
  ],
      	
  	
  	
  	
  ...
      });




                                                         * a lightweight object until then
</aside>
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
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
note:
list already
   scrollable
Data modeling

3_data_modeling
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
...returns a nested JSON array




            mmm, json
Apigee API console


                     ‘businesses’
                      array
"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
]
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
<aside>
      Models can be associated
         with other models


 Fields can also have default values,
 conversion functions, and validation


                          </aside>
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’
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
Create constants
                                              please change this!
<script>
	
  	
  	
  	
  YELP_KEY	
  =	
  'G3HueY_I5a8WZX-­‐_bAAAA';
	
  	
  	
  	
  DEFAULT_CITY	
  =	
  'San	
  Francisco';
	
  	
  	
  	
  BUSINESS_TYPE	
  =	
  'Bars';
</script>
We can make the proxy URL dynamic,
   which would allow geolocation.


     But this requires an async
         callback sequence.
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
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
cheeky callback

store



records
List Binding

4_list_binding
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
:-(




but we haz
  records!
another component query
CB.getCity(function	
  (city)	
  {

	
  	
  	
  	
  cards.query('#listCard	
  toolbar')[0]
	
  	
  	
  	
  	
  	
  	
  	
  .setTitle(city	
  +	
  '	
  '	
  +	
  BUSINESS_TYPE);

	
  	
  	
  	
  ...

                               now title will always match city
List items are templated

 {
 	
  	
  	
  	
  id:	
  'dataList',
 	
  	
  	
  	
  xtype:	
  'list',
 	
  	
  	
  	
  store:	
  null,
 	
  	
  	
  	
  itemTpl:	
  '{name}'
 }



   model fields in curly braces
Spinner bound to store
instantiate mask                               over body

        Ext.create('Ext.LoadMask',	
  Ext.getBody(),	
  {
        	
  	
  	
  	
  store:	
  store,
        	
  	
  	
  	
  msg:	
  ''
        });
                                will show when store is loading
The City Bars App with Sencha Touch 2
A more interesting template
itemTpl:
	
  	
  	
  	
  '<img	
  class="photo"	
  src="{photo_url}"	
  width="40"	
  height="40"/>'	
  +
	
  	
  	
  	
  '{name}<br/>'	
  +
	
  	
  	
  	
  '<img	
  src="{rating_img_url_small}"/>&nbsp;'	
  +
	
  	
  	
  	
  '<small>{address1}</small>'




             HTML allowed
Hack the style
<style>
	
  	
  	
  	
  .photo	
  {
	
  	
  	
  	
  	
  	
  	
  	
  float:left;
	
  	
  	
  	
  	
  	
  	
  	
  margin:0	
  8px	
  16px	
  0;
	
  	
  	
  	
  	
  	
  	
  	
  border:1px	
  solid	
  #ccc;
	
  	
  	
  	
  	
  	
  	
  	
  -­‐webkit-­‐box-­‐shadow:	
  0	
  2px	
  4px	
  #777;
	
  	
  	
  	
  }
</style>
...width="40"	
  height="40"	
  />




                                     seems like
                                     a waste
src.sencha.io
<img	
  src="http://src.sencha.io/40/{photo_url}"
	
  	
  	
  	
  	
  width="40"	
  height="40"
/>




                                           4 times
                                           smaller
List Event Handler

5_list_event_handler
{                                                                 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
       	
  	
  	
  	
  }
       }
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
       	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }
       	
  	
  	
  	
  	
  	
  	
  	
  }
       	
  	
  	
  	
  }],	
  ...
The City Bars App with Sencha Touch 2
Detail Page

6_detail_page
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>'
         	
  	
  	
  	
  ]
         }]
:-(
Remember this?
setData does not cascade into child items!


      CB.cards.getActiveItem().setData(
      	
  	
  	
  	
  records[0].data
      );
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
good




not so much
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;
            }
The City Bars App with Sencha Touch 2
One ��nal tweak
move from        {
inner panel...   	
  	
  	
  	
  //	
  textual	
  detail
                 	
  	
  	
  	
  cls:	
  'detail',
                 	
  	
  	
  	
  styleHtmlContent:	
  true,
                 	
  	
  	
  	
  ...




                 {
                 	
  	
  	
  	
  //	
  the	
  details	
  card   ...to outer card
                 	
  	
  	
  	
  id:	
  'detailCard',
                 	
  	
  	
  	
  styleHtmlContent:	
  true,
complete with
<h2> </h2>
The City Bars App with Sencha Touch 2
The City Bars App with Sencha Touch 2
Development sequence
1 App Architecture   5 List Event Handler

2 UI Structure       6 Detail Page

3 Data Modeling      7 Customize Theme

4 List Binding
Other ideas...
‘Responsive’ Apps




http://sencha.com/x/cv
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
Geolocation
O ine app




http://github.com/jamesgpearce/confess
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
Debugging




http://phonegap.github.com/weinre
James Pearce
@ jamespearce

More Related Content

The City Bars App with Sencha Touch 2

  • 1. 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                        }]                });        } });
  • 28. Layouts card vbox fit hbox
  • 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
  • 35. note: list already scrollable
  • 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
  • 38. ...returns a nested JSON array mmm, json
  • 39. Apigee API console ‘businesses’ array
  • 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
  • 52. :-( but we haz records!
  • 53. another component query CB.getCity(function  (city)  {        cards.query('#listCard  toolbar')[0]                .setTitle(city  +  '  '  +  BUSINESS_TYPE);        ... now title will always match city
  • 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}"/>&nbsp;'  +        '<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>
  • 59. ...width="40"  height="40"  /> seems like a waste
  • 60. src.sencha.io <img  src="http://src.sencha.io/40/{photo_url}"          width="40"  height="40" /> 4 times smaller
  • 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>'        ] }]
  • 67. :-(
  • 68. Remember this? setData does not cascade into child items! CB.cards.getActiveItem().setData(        records[0].data );
  • 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