Sencha Touch

10,695 views

Published on

Talk I did at the Wellington JS Meetup on 24 Feb 2011 on Sencha Touch and PhoneGap.

Published in: Technology
1 Comment
4 Likes
Statistics
Notes
No Downloads
Views
Total views
10,695
On SlideShare
0
From Embeds
0
Number of Embeds
30
Actions
Shares
0
Downloads
312
Comments
1
Likes
4
Embeds 0
No embeds

No notes for slide

Sencha Touch

  1. 1. Craig Walker, Chief Technology Officer www.xero.com
  2. 2. Craig Walker, Chief Technology Officer www.xero.com
  3. 3. What is Xero?http://www.xero.com/signup/
  4. 4. BlackBerry 6
  5. 5. BlackBerry 6 2nd half 2011
  6. 6. Lots of frameworks…• Zepto.js• DynamicX• SproutCore• XUI• Appcelerator• iUI• iWebKit• jQuery Mobile• jQTouch• And lots more…
  7. 7. Sencha Touch
  8. 8. What is Sencha Touch? A JavaScript framework forbuilding rich mobile applications
  9. 9. Why Sencha Touch?• Cross-platform• Looks native, feels native• Faster, cheaper, easier to build with• Highly customisable• Flexible deployment• HTML5/CSS3 goodness
  10. 10. Yes - but WHY Sencha Touch?
  11. 11. Tap !== Click
  12. 12. Touch Event Manager• Built on native events• Abstracted for performance• Multi-touch & gesture support
  13. 13. Touch Event Manager Ext.fly("el").on({ tap: function() { alert("You tapped me"); }, pinch: function() { alert("You pinched me"); }, swipe: function() { alert("Stop touching me...") } }); 28
  14. 14. Scroll Event Manager• Scrolling with momentum & bounce physics• Native & natural• Hardware accelerated
  15. 15. UI Toolkit
  16. 16. Buttons
  17. 17. FormsSliders
  18. 18. Pickers
  19. 19. Lists
  20. 20. NestedLists
  21. 21. ToolbarsTabs
  22. 22. PanelsCarousels
  23. 23. Maps
  24. 24. Overlays
  25. 25. Layouts• Container layout specifies how its children components are rendered
  26. 26. fit
  27. 27. card
  28. 28. vbox
  29. 29. hbox
  30. 30. MVC Routes Controllers Models Views Stores
  31. 31. Theming• SASS & Compass – sass-lang.com – compass-style.org• CSS3 is awesome – SCSS is awesomer
  32. 32. SCSS CSS$blue: #3bbfce; /* line 5, variables.scss */$margin: 16px; .example1 {$padding: 4px; border-color: #3bbfce; }.example1 { border-color: $blue; /* line 9, variables.scss */} .example2 { margin: 16px;.example2 { color: #3bbfce; margin: $margin; } color: $blue;} /* line 14, variables.scss */ .example3 {.example3 { margin: 32px; margin: ($margin / 2px) * $padding; }}
  33. 33. SCSS CSS@mixin add-child($color) { /* line 18, mixins.scss */ color: $color; .example { background-color: lighten($color, 50); color: red; background-color: white; .child { } padding: 5px; /* line 5, mixins.scss */ .example .child { &:first { padding: 5px; background-color: darken($color, 10) } }; /* line 8, mixins.scss */ .example .child:first { span { background-color: #cc0000; color: mix($color, blue); } } /* line 12, mixins.scss */ } .example .child span {} color: #7f007f; }.example { @include add-child(#F00);}
  34. 34. SCSS CSS@import "compass"; /* line 5, gradients.scss */ .button {$width: 100px; width: 100px; }.button { /* line 8, gradients.scss */ width: $width; .button .round { -moz-border-radius: 5px; .round { -webkit-border-radius: 5px; @include border-radius(5px); -o-border-radius: 5px; } -ms-border-radius: 5px; -khtml-border-radius: 5px; .linear { border-radius: 5px; @include linear-gradient( } color-stops(white, #c39 30%, /* line 12, gradients.scss */ #b7f 70%, #aaa) .button .linear { ); background-image: -webkit-gradient( } linear, 0% 0%, 0% 100%,} color-stop(0%, #ffffff), color-stop(50%, #cc3399), color-stop(100%, #bb77ff)); background-image: -moz-linear-gradient(top, #ffffff 0%, #cc3399 50%, #bb77ff 100%); background-image: linear-gradient(top, #ffffff 0%, #cc3399 50%, #bb77ff 100%); }
  35. 35. Let’s look at some code...
  36. 36. Demo: From Desktop to Mobile
  37. 37. index.html<!doctype html><html><head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Xero Help</title> <link rel="stylesheet" href="resources/css/xerohelp.css" type="text/css"></head><body> <script type="text/javascript" src="lib/sencha-touch-debug.js"></script> <script type="text/javascript" src="app/xerohelp.js"></script></body></html>
  38. 38. app/xerohelp.js// utilsdocument.write(<script type="text/javascript" src="app/utils/string.js"></script>);// applicationdocument.write(<script type="text/javascript" src="app/routes.js"></script>);document.write(<script type="text/javascript" src="app/app.js"></script>);// modelsdocument.write(<script type="text/javascript" src="app/models/TOC.js"></script>);document.write(<script type="text/javascript" src="app/models/HelpFile.js"></script>);// viewsdocument.write(<script type="text/javascript" src="app/views/Viewport.js"></script>);document.write(<script type="text/javascript" src="app/views/TOCPanel.js"></script>);document.write(<script type="text/javascript" src="app/views/HelpCarousel.js"></script>);document.write(<script type="text/javascript" src="app/views/HelpPanel.js"></script>);// controllersdocument.write(<script type="text/javascript" src="app/controllers/help.js"></script>);
  39. 39. app/routes.jsExt.Router.draw(function(map) { map.connect("/help/home", {controller: help, action: home}); map.connect("/help/:id", {controller: help, action: show});});
  40. 40. app/app.jsExt.regApplication({ name: "XERO", defaultUrl: /help/home, defaultTarget: "viewport", icon: resources/images/icon.png, glossOnIcon: false, region: "NZ", apiUrl: "http://help.stage.xero.com/api", launch: function() { this.viewport = new XERO.Viewport({ application: this }); }});
  41. 41. XERO.Viewport = Ext.extend(Ext.Panel, { id: viewport, app/views/viewport.js layout: card, fullscreen: true, initComponent: function() { Ext.apply(this, { dockedItems: [{ xtype: "toolbar", dock : "top", title: "Help Center", itemId: "help-toolbar" },{ xtype: "toolbar", dock: "bottom", items: [{ xtype: "button", text: "Index", handler: this.onTOCButtonTap, scope: this },{ xtype: "spacer" }, { xtype: "button", text: "Switch Region", handler: this.onSwitchRegionTap, scope: this }] }] }); XERO.Viewport.superclass.initComponent.apply(this, arguments); }, ...
  42. 42. setTitle: function(title) { this.down("#help-toolbar").setTitle(title); }, app/views/viewport.js onTOCButtonTap: function() { if(! this.tocPanel) { this.tocPanel = Ext.create({ xtype: "tocpanel" }); } this.tocPanel.show(); }, onSwitchRegionTap: function() { if(! this.regionPicker) { this.regionPicker = new Ext.Picker({ slots: [{ name : region, title: Switch Region, data : [ {text: New Zealand, value: "NZ"}, {text: Australia, value: "AUS"}, {text: United Kingdom, value: "UK"}, {text: Global, value: "INT"} ] }] }); } this.regionPicker.show(); }});
  43. 43. app/controllers/help.jsExt.regController("help", { home: function(request) { this.show(Ext.apply(request, { id: "home" })); }, show: function(request) { if(! this.helpCarousel) { this.helpCarousel = this.render({ xtype: helpcarousel }); } XERO.viewport.setActiveItem(this.helpCarousel); XERO.viewport.setTitle("Loading..."); this.helpCarousel.loadHelpPage(request.id, request.historyUrl); }});
  44. 44. app/models/helpfile.jsExt.regModel("HelpFile", { fields: [ { name: "id", type: "int" }, "abstractText", "body", "heading", "helpPage", { name: "createdDateUTC", type: "date", format: "M$" }, { name: "success", type: "boolean" } ] /*, validations: [ { type: "presence", field: "helpPage" } ], hasMany: [ { model: "Region", name: "regions" } ] */});
  45. 45. app/models/toc.jsExt.regModel("TOC", { fields: [ "id", "helpPage", "href", "text" ], proxy: { type: scripttag, url: String.format("{0}/toc/", XERO.apiUrl), reader: { type: "tree", root: "items" } }});(function() { new Ext.data.TreeStore({ storeId: "TOCStore", model: "TOC", autoLoad: true });}());
  46. 46. XERO.views.HelpCarousel = Ext.extend(Ext.Carousel, { cls: "cards", app/views/helpcarousel.js layout: "fit", initComponent: function() { XERO.views.HelpCarousel.superclass.initComponent.apply(this, arguments); }, onRender: function() { XERO.views.HelpCarousel.superclass.onRender.apply(this, arguments); if(this.el) { this.mon(this.el, { tap: this.onTap, click: Ext.emptyFn, delegate: "a", stopEvent: true, scope: this }); this.mon(this, { cardswitch: function() { this.onHelpSwitch(this.getActiveItem().helpPage); }, scope: this }); } },
  47. 47. loadHelpPage: function(id, href) { var bookmark; app/views/helpcarousel.js if(href.indexOf("$") != -1) { bookmark = href.right(href.length - href.indexOf("$") - 1); } var item = this.getComponent(id); if(item) { this.setActiveItem(item); if(bookmark){ item.scrollToBookmark(bookmark); } } else { var panel = this.add({ xtype: "helppanel", id: id, goToBookmark: bookmark, listeners: { helploaded: this.onHelpSwitch, single: true, scope: this } }); this.doComponentLayout(); this.setActiveItem(panel); }},
  48. 48. onTap: function(e, target) { var id = target.getAttribute(xero:id), app/views/helpcarousel.js type = target.getAttribute(xero:type), bookmark = target.getAttribute(xero:bookmark), url = target.getAttribute(xero:path); if(type && type.toLowerCase() == "help") { if(bookmark) { this.getActiveItem().scrollToBookmark(bookmark); } else { Ext.dispatch({ controller: "help", action: "show", id: id, historyUrl: target.href }); } } else { if(target.href.toLowerCase().startsWith("mailto:")) { location.href = target.href; } else { window.open(target.href); } } }});Ext.reg(helpcarousel, XERO.views.HelpCarousel);
  49. 49. app/views/helppanel.jsXERO.views.HelpPanel = Ext.extend(Ext.Panel, { cls: "help-panel", layout: "fit", scroll: "vertical", styleHtmlContent: true, goToBookmark: null, html: <div class="loading-indicator"> </div>, initComponent: function() { this.addEvents("helploaded"); XERO.views.HelpPanel.superclass.initComponent.apply(this, arguments); }, onRender: function() { XERO.views.HelpPanel.superclass.onRender.apply(this, arguments); this.loadHelpPage(); },...
  50. 50. loadHelpPage: function() { Ext.util.JSONP.request({ url: String.format("{0}/help/", XERO.apiUrl), app/views/helppanel.js callbackKey: "callback", params: { helpPage: this.id }, callback: function(doc) { this.helpPage = doc; this.title = doc.heading; this.update(this.helpPage.body); if(this.goToBookmark) { this.scrollToBookmark(this.goToBookmark); } this.fireEvent("helploaded", this.helpPage); }, scope: this }); }, scrollToBookmark: function(bookmark) { var el = this.getEl().down(String.format(a[name="{0}"], bookmark)); if (el) { this.scrollIntoView(el); } }, });Ext.reg(helppanel, XERO.views.HelpPanel);
  51. 51. XERO.views.TOCPanel = Ext.extend(Ext.NestedList, { title: "Index", showAnimation: { app/views/helppanel.js type: "slide", direction: "up" }, floating: true, initComponent: function() { this.store = Ext.getStore("TOCStore"); this.toolbar = {...}; XERO.views.TOCPanel.superclass.initComponent.apply(this, arguments); this.mon(this, { selectionchange: function(list, selection) { var record = selection[0]; if(record.get("leaf") === true) { Ext.dispatch({ controller: "help", action: "show", id: record.get("helpPage"), historyUrl: String.format("/help/{0}", record.get("helpPage")) }); this.hide(); } }, scope: this }); }});
  52. 52. Touchable help...
  53. 53. going native
  54. 54. • Cross-platform• Open source• Extensible• Instantiates chromeless web view• Adds JavaScript access to native APIs
  55. 55. Web AppPhoneGap etc…
  56. 56. Native APIs• Device identification• Network access• Sensors• Camera/image sources• Contacts• File access
  57. 57. Everything else?• HTML for layout• JavaScript for accessing device APIs• CSS for look & feel• Offline storage for standalone clients• Ajax, JSONP for syncing to the cloud – Runs on file:// protocol which is exempt from same-origin policy• Just use Sencha Touch!
  58. 58. PhoneGap Build
  59. 59. Tips & Tricks
  60. 60. Tools• Browsers – Safari the best (unfortunately)• Web Inspector• RemoteJS (Android debugging)• Souders’ bookmarklets – stevesouders.com/mobileperf• Jdrop – jdrop.org
  61. 61. Object-oriented• Use namespaces to define your library• Define components – code for reusability• Extend first, write plugins second (not at all if possible)
  62. 62. Events rock!• Use events to communicate between components• Use event delegation
  63. 63. Override appropriately• Do not edit the library files• DO NOT EDIT THE LIBRARY FILES!• Use an overrides file if you need to override the framework• Do the same with CSS (but you should be using cls, ui properties)
  64. 64. Define a directory structure• Break your code into small files• Use build tools to compile for performance• Use sencha-touch-debug.js during dev (but never prod!)• Keep the framework up-to-date – upgrade as often as you can
  65. 65. Worry about performance• Understand client-side performance rules & use them• Latency bad• JIT compilers – compilation time relates to size of file the method exists in• Keep DOM light• Destroy components that aren’t visible• concatenate, minify, compress!
  66. 66. Theming/Layouts• Use SCSS• Remove unnecessary CSS by only including required SCSS mixins• Understand XTemplate• Understand doComponentLayout
  67. 67. Sencha.comRead the forums Read the docs Read the source!
  68. 68. Any questions? www.xero.com
  69. 69. www.xero.com/careers/

×