The City Bars App with Sencha Touch 2

14,089 views
13,833 views

Published on

Published in: Technology, Design
1 Comment
12 Likes
Statistics
Notes
No Downloads
Views
Total views
14,089
On SlideShare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
338
Comments
1
Likes
12
Embeds 0
No embeds

No notes for slide

The City Bars App with Sencha Touch 2

  1. 1. The City Bars App withSencha Touch 2
  2. 2. Sencha Touch A JavaScript framework for buildingrich mobile apps with web standards
  3. 3. http://sencha.com/x/d5http://sencha.com/x/ed
  4. 4. Basically...Get a WebKit-based desktop browser Get some emulators & real devicesDownload the Sencha Touch 2 PR2 SDK Develop against a local web server Optional, but highly recommended!
  5. 5. http://sencha.com/touch
  6. 6. stylesheetscript framework
  7. 7. Introducing the City Bars App
  8. 8. http://sencha.com/x/eehttp://senchalearn.github.com/citybars2
  9. 9. 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/
  10. 10. http://github.com/senchalearn/citybars2
  11. 11. Development sequence1 App Architecture 5 List Event Handler2 UI Structure 6 Detail Page3 Data Modeling 7 Customize Theme4 List Binding
  12. 12. Application Architecture1_app_architecture
  13. 13. applicationentry pointJS + CSS in SDK
  14. 14. 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
  15. 15. 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 });
  16. 16. UI Structure2_ui_structure
  17. 17. toolbar back toolbardataList clicklistCard detailCard CB.cards
  18. 18. 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                        }]                });        } });
  19. 19. <aside> about layoutsand components
  20. 20. Layoutscard vboxfit hbox
  21. 21. Child component patterns I var  list  =  new  Ext.List({        store:  store,        ... }); instantiate component var  panel  =  new  Ext.Panel({        items:  [list,  ...],        ... }); reference component by var
  22. 22. Child component patterns II preferable in ST2 var  list  =  Ext.create(Ext.List,  {        store:  store,        ... }); var  panel  =  Ext.create(Ext.Panel,  {        items:  [list,  ...]        ... });
  23. 23. Child component patterns III var  panel  =  Ext.create(Ext.Panel,  {        items:  [                {                        xtype:  list,                        store:  store,                        ...                },  ... deferred creation*        ],        ... }); * a lightweight object until then
  24. 24. </aside>
  25. 25. 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  itselflist*                //  gets  bound  to  the  store  once  city  known                id:  dataList,                xtype:  list        }] } * list will be bound to a store later
  26. 26. 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
  27. 27. note:list already scrollable
  28. 28. Data modeling3_data_modeling
  29. 29. The YELP API... http://api.yelp.com/business_review_search ?ywsid=YELP_KEY &term=BUSINESS_TYPE free, rate limited &location=CITYbusiness type, and city name
  30. 30. ...returns a nested JSON array mmm, json
  31. 31. Apigee API console ‘businesses’ array
  32. 32. "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]
  33. 33. Create a data model give the ‘class’ a nameExt.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
  34. 34. <aside> Models can be associated with other models Fields can also have default values, conversion functions, and validation </aside>
  35. 35. Create a model storecreate the store var  store  =  Ext.create(Ext.data.Store,  {        model:  "Business",        ... }); containing this type of modelThink of a store as a ‘table’ of model instance ‘rows’
  36. 36. 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
  37. 37. Create constants please change this!<script>        YELP_KEY  =  G3HueY_I5a8WZX-­‐_bAAAA;        DEFAULT_CITY  =  San  Francisco;        BUSINESS_TYPE  =  Bars;</script>
  38. 38. We can make the proxy URL dynamic, which would allow geolocation. But this requires an async callback sequence.
  39. 39. 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
  40. 40. 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
  41. 41. cheeky callbackstorerecords
  42. 42. List Binding4_list_binding
  43. 43. our 2 async functions//  get  the  cityCB.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
  44. 44. :-(but we haz records!
  45. 45. another component queryCB.getCity(function  (city)  {        cards.query(#listCard  toolbar)[0]                .setTitle(city  +      +  BUSINESS_TYPE);        ... now title will always match city
  46. 46. List items are templated {        id:  dataList,        xtype:  list,        store:  null,        itemTpl:  {name} } model fields in curly braces
  47. 47. Spinner bound to storeinstantiate mask over body Ext.create(Ext.LoadMask,  Ext.getBody(),  {        store:  store,        msg:   }); will show when store is loading
  48. 48. A more interesting templateitemTpl:        <img  class="photo"  src="{photo_url}"  width="40"  height="40"/>  +        {name}<br/>  +        <img  src="{rating_img_url_small}"/>&nbsp;  +        <small>{address1}</small> HTML allowed
  49. 49. Hack the style<style>        .photo  {                float:left;                margin:0  8px  16px  0;                border:1px  solid  #ccc;                -­‐webkit-­‐box-­‐shadow:  0  2px  4px  #777;        }</style>
  50. 50. ...width="40"  height="40"  /> seems like a waste
  51. 51. src.sencha.io<img  src="http://src.sencha.io/40/{photo_url}"          width="40"  height="40"/> 4 times smaller
  52. 52. List Event Handler5_list_event_handler
  53. 53. { 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        } }
  54. 54. 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  buttonarrow style                //  that  slides  back  to  list  card                text:  Back,                ui:  back, back to list                listeners:  {                        tap:  function  ()  {                                CB.cards.setActiveItem(0); when tapped                        }                }        }],  ...
  55. 55. Detail Page6_detail_page
  56. 56. 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>        ] }]
  57. 57. :-(
  58. 58. Remember this?setData does not cascade into child items! CB.cards.getActiveItem().setData(        records[0].data );
  59. 59. Override setData set title on toolbarsetData:  function  (data)  {        this.query(toolbar)[0].setTitle(data.name);        this.query([cls="detail"])[0].setData(data);}, apply data to template on inner panel
  60. 60. goodnot so much
  61. 61. 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; }
  62. 62. One final tweakmove from {inner panel...        //  textual  detail        cls:  detail,        styleHtmlContent:  true,        ... {        //  the  details  card ...to outer card        id:  detailCard,        styleHtmlContent:  true,
  63. 63. complete with<h2> </h2>
  64. 64. Development sequence1 App Architecture 5 List Event Handler2 UI Structure 6 Detail Page3 Data Modeling 7 Customize Theme4 List Binding
  65. 65. Other ideas...
  66. 66. ‘Responsive’ Appshttp://sencha.com/x/cv
  67. 67. PackagingAdd to home screen- Icon- Splash screenHybrid app; PhoneGap / NimbleKit- Contacts API- Geolocationhttp://sencha.com/x/cyhttp://sencha.com/x/de
  68. 68. Geolocation
  69. 69. O ine apphttp://github.com/jamesgpearce/confess
  70. 70. O ine dataTaking Yelp data o ineTaking images o ine- src.sencha.io to generate cross-origin B64Detecting network connection changes http://sencha.com/x/df
  71. 71. Debugginghttp://phonegap.github.com/weinre
  72. 72. James Pearce@ jamespearce

×