The Art and Science of Shipping Ember Apps
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

The Art and Science of Shipping Ember Apps

  • 248 views
Uploaded on

As an alternative to other popular client MVC solutions like Backbone.js and Angular.js, Ember.js differs in that it provides 'Rails-like' defaults by convention to common coding patterns,......

As an alternative to other popular client MVC solutions like Backbone.js and Angular.js, Ember.js differs in that it provides 'Rails-like' defaults by convention to common coding patterns, intelligent memory management, built-in integration testing, and numerous, next generation client side persistence solutions. Join O'Reilly author, Jesse Cravens, as he presents information from his new book: O'Reilly's 'Building Web Apps with Ember.js’ as he takes the audience through the construction of the RocknRollCall demo application. Construct a workflow using the latest in JavaScript build and package management solutions. Use Handlebars and Ember templates.

More in: Software
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
248
On Slideshare
243
From Embeds
5
Number of Embeds
2

Actions

Shares
Downloads
7
Comments
0
Likes
1

Embeds 5

http://www.slideee.com 4
https://www.linkedin.com 1

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. 8/6/14 THE ART AND SCIENCE OF SHIPPING SINGLE PAGE APPS WITH EMBER.JS UX DEVELOPMENT at frog
  • 2. @jdcravens github.com/jessecravens jessecravens.com principal web architect | frog Austin
  • 3. ART & SCIENCE
  • 4. THE ART & SCIENCE OF SHIPPING SINGLE PAGE APPS WITH EMBER.JS
  • 5. 2006
  • 6. 2007
  • 7. ! WHY? ! ! “YOU HAVE TO KNOW THE PAST TO UNDERSTAND THE PRESENT.” ! - DR. CARL SAGAN ! "STUDY THE PAST IF YOU WOULD DEFINE THE FUTURE...." ! - CONFUCIUS ! ! ! ! !
  • 8. TEMPLATES TRADITIONAL 14 ROUTER UI HTML5/CSS3/JavaScript Web Framework HTML5/CSS3/JavaScript Data Services JSON/XML Storage DBs App Server refresh refresh refresh refresh refresh
  • 9. App Server EARLY SPA IMPLEMENTATION 15 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Data Services JSON/XML Render Engine JavaScript DOM creation && String templates Gadget App iFrame initial load XHR RESPONSE -JSON MARKUP FORMAT XHR REQUEST -JSON ACTION OBJECT Gadget App iFrame Gadget App iFrame REQ PARAMs Iframe Refresh
  • 10. BACK IN THE DAY … A NAIVE IMPLEMENTATION ! ! SEGREGATED DESIGN TEAM HANDED OFF PHOTOSHOP COMPS ! ARCHITECTED SERVER IMPLEMENTATION FIRST ! LACK OF CONVENTIONS AND DEVELOPER ERGONOMICS ! YUI MODULE PATTERN PSEUDO CLASSICAL INHERITANCE ! JSON MARK UP LANGUAGE CUSTOM RENDER ENGINE ! HTML STRINGS AND VARIABLES DOM CREATION ! CONTINUATION PASSING AND CALLBACK HELL ! IFRAMES AND GADGET SPEC ! ! ! ! ! ! !
  • 11. App Server INITIAL LOAD 19 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Data Services JSON/XML Render Engine JavaScript DOM creation && String templates Gadget App 1 iFrame initial load Gadget App 2 iFrame Gadget App 3 iFrame REQ PARAMs Iframe Refresh
  • 12. INITIAL LOAD 20 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates initial load
  • 13. INITIAL LOAD 21 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates initial load { "root": “#portal", "childrenType": "Portal", "type": "ClientCommandObject", "children": [{ "childrenType": "TabContainer", "type": "Portal", "children": [{ "childrenType": "TabPage", "type": "TabContainer", "children": [{ "children": [{ "childrenType": "Gadget", "type": "Column", "children": [{ "gadgetType": "MyAccounts", "gadgetContentType": { "type": "url" } }, { "gadgetType": "EntOffersMlp", "gadgetContentType": { "type": "url" } }, { "gadgetType": "SpendingPlan", "gadgetContentType": { "type": "url" } }, { "gadgetType": "ImcoStorefront", "gadgetContentType": { "type": "url" } },
  • 14. INITIAL LOAD 22 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates initial load Main.objects.renderObject(rootEl, configHash);
  • 15. INITIAL LOAD 23 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates initial load Main.objects.renderObject = function(root, obj){ this.rootObj = root; // here you make the call to a function to build out your proper div, // this will also append it to the root ! var currentObject = this.constructLayoutObject(this.rootObj, obj); if(obj.children == null || obj.children.length == 0){ return; } else{ // render the branches of the object tree to the root by a recursive call var i; for(i=0; i < obj.children.length; i++){ this.renderObject(currentObject, obj.children[i]); } } };
  • 16. INITIAL LOAD 24 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates initial load Main.createNode: function(type, id, classNames) { var node = document.createElement(type); node.id = id; if (typeof classNames === 'string' ) { node.className = classNames; } else if (typeof classNames === 'object' ){ var str = classNames.toString(); var classString=str.replace(/,/g,' '); node.className = classString; } return node; }
  • 17. INITIAL LOAD 25 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates initial load ! gadgets.Gadget.prototype.getContent = function(continuation) { gadgets.callAsyncAndJoin( [this.getTitleBarContent, this.getUserPrefsDialogContent, this.getMainContent], function(results) {continuation(results.join(''));}, this); }; ! gadgets.Gadget.prototype.render = function(chrome) { if (chrome) { this.getContent(function(content) { chrome.innerHTML = content; }); } }; !
  • 18. App Server LOADED 26 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Data Services JSON/XML Render Engine JavaScript DOM creation && String templates Gadget App 1 iFrame Gadget App 2 iFrame Gadget App 3 iFrame REQ PARAMs Iframe Refresh
  • 19. POST LOADED 27 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates DRAG N’ DROP EVENT FIRES! Gadget App 1 iFrame Gadget App 2 iFrame Gadget App 3 iFrame REQ PARAMs Iframe Refresh
  • 20. POST LOADED 28 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates Gadget App 1 iFrame Gadget App 2 iFrame Gadget App 3 iFrame REQ PARAMs Iframe Refresh x
  • 21. POST LOADED 29 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates Main.moveGadget = function(obj) { var serviceUrlConfigObject = obj; serviceUrlConfigObject.position = (obj.position !== undefined)?obj.position:0; serviceUrlConfigObject.action = "moveObject"; serviceUrlConfigObject.version = 1; Main.services.takeAction('handleMoveGadget' , serviceUrlConfigObject); }; REQ PARAMs Iframe Refresh Gadget App 1 iFrame Gadget App 2 iFrame Gadget App 3 iFrame x
  • 22. POST LOADED 30 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates { action:”moveGadget", version:1, category:category, order:order }; XHR REQUEST -JSON ACTION OBJECT Gadget App 1 iFrame Gadget App 2 iFrame Gadget App 3 iFrame x REQ PARAMs Iframe Refresh
  • 23. REFRESH 31 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates { "root": “#portal", "childrenType": "Portal", "type": "ClientCommandObject", "children": [{ "childrenType": "TabContainer", "type": "Portal", "children": [{ "childrenType": "TabPage", "type": "TabContainer", "children": [{ "children": [{ "childrenType": "Gadget", "type": "Column", "children": [{ "gadgetType": "MyAccounts", "gadgetContentType": { "type": "url" } }, { "gadgetType": "EntOffersMlp", "gadgetContentType": { "type": "url" } }, { "gadgetType": "SpendingPlan", "gadgetContentType": { "type": "url" } }, { "gadgetType": "ImcoStorefront", "gadgetContentType": { "type": "url" } }, XHR RESPONSE -JSON MARKUP FORMAT Gadget App 1 iFrame Gadget App 2 iFrame Gadget App 3 iFrame x REQ PARAMs Iframe Refresh
  • 24. App Server LOADED 32 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Data Services JSON/XML Render Engine JavaScript DOM creation && String templates Gadget App 1 iFrame Gadget App 2 iFrame Gadget App 3 iFrame REQ PARAMs Iframe Refresh MoveGadget ChangePage OpenPanel AddGadget DeleteGadget etc.
  • 25. WE REALLY NEED RAILS IN THE BROWSER!
  • 26. 2014
  • 27. EMBER 37 Views HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Ember.App JavaScript Ember Data JavaScript ROUTER JavaScript Local Storage JavaScript initial load XHR -JSON WebSocket TEMPLATES .hbs App Server Data Services JSON/XML Models JavaScript Controllers JavaScript Backburner JavaScript Components JavaScript present
  • 28. NxGEN: THE NEW S&P CAPITAL IQ
  • 29. Working with the S&P labs team we sketched and visualized 5 key concepts. One of the key concepts was a way to analyze and view portfolios through different lenses.
  • 30. BACK IN THE DAY … A NAIVE IMPLEMENTATION ! ! SEGREGATED DESIGN TEAM HANDED OFF PHOTOSHOP COMPS ! ARCHITECTED SERVER IMPLEMENTATION FIRST ! LACK OF CONVENTIONS AND DEVELOPER ERGONOMICS ! YUI MODULE PATTERN PSEUDO CLASSICAL INHERITANCE ! JSON MARK UP LANGUAGE CUSTOM RENDER ENGINE ! HTML STRINGS AND VARIABLES DOM CREATION ! CONTINUATION PASSING AND CALLBACK HELL ! IFRAMES AND GADGET SPEC ! ! ! ! ! ! !
  • 31. PRESENT AND FUTURE ! DESIGN WITH CODE ! WORK FRONT TO BACK ! THE EMBER WAY ! THE EMBER OBJECT MODEL ! EMBER RUN LOOP AND BACKBURNER ! JS TEMPLATES W/ HANDLEBARS / HTMLBARS ! PROMISES AND THE ASYNC ROUTER ! WEB COMPONENTS ! ! ! ! ! ! !
  • 32. DESIGN WITH CODE
  • 33. SKETCH ADOBE EDGE REFLOW WEBFLOW MACAW !
  • 34. SKETCH ADOBE EDGE REFLOW WEBFLOW MACAW !
  • 35. CHROME DEV TOOLS !
  • 36. WORK FRONT TO BACK
  • 37. BACK END TEAM RAPID DEV 57 Early Design: HTML, Diagram Controllers URL Driven: State Manager and Routes First Populate the Controllers with Dummy Data Models w/ Fixtures FixtureAdapter FRONT END TEAM Models w/ RESTAdapter Build Server-Side Routes/Endpoints Serialization and Formatting JSON Remote Data Store and DB Sync ! ! FixtureAdapter RESTAdapter
  • 38. RAPID DEV REST ADAPTER TEMPLATES FIXTURE ADAPTER ROUTER ROUTE HANDLERS CONTROLLERS VIEWS/COMPONENTS MODELS SERVICES (API DESIGN) MVC, SPA (Bootstrap Object) SERVICES (IMPLEMENTATION) FRONT END DEVELOPMENT WEB/SERVICES DEVELOPMENT D E P L O Y C O N C E P T U A L
  • 39. EMBER DATA ADAPTERS !
  • 40. ADAPTERS 60 App.ApplicationAdapter = DS.FixtureAdapter.extend({ namespace: 'rocknrollcall' }); ! App.ApplicationAdapter = DS.LSAdapter.extend({ namespace: 'rocknrollcall' }); ! App.ActivityAdapter = DS.LSAdapter.extend({ namespace: 'rocknrollcall' }); ! App.ApplicationAdapter = DS.RESTAdapter.extend({ namespace: 'rocknrollcall' });
  • 41. NxGEN: THE NEW S&P CAPITAL IQ
  • 42. API STUBS !
  • 43. EMBER APP KIT 63 { "name": "app-kit", "namespace": "appkit", "APIMethod": "stub", …
  • 44. ROUTES.JS 64 module.exports = function(server) { ! // Create an API namespace, so that the root does not // have to be repeated for each end point. server.namespace("/api", function() { ! // Return fixture data for "/api/activities" server.get("/activities", function(req, res) { var activities = [ ]; }; res.send(activities); }); }); };
  • 45. EMBER APP KIT 65 { "name": "app-kit", "namespace": "appkit", "APIMethod": "stub", … ! { "name": "app-kit", "namespace": "appkit", "APIMethod": “proxy”, "proxyURL": "http://whatever.api:3232", ...
  • 46. EMBRACE THE EMBER WAY
  • 47. ! ! FRIENDS OR FOES ACTIVE GENERATION ! NAMING CONVENTIONS ! !
  • 48. ACTIVE GENERATION
  • 49. EMBER APPLICATION 70 App = Ember.Application.create({ ! ENV.LOG_MODULE_RESOLVER = true; ENV.APP.LOG_RESOLVER = true; ENV.APP.LOG_ACTIVE_GENERATION = true; ENV.APP.LOG_MODULE_RESOLVER = true; ENV.APP.LOG_TRANSITIONS = true; ENV.APP.LOG_TRANSITIONS_INTERNAL = true; ENV.APP.LOG_VIEW_LOOKUPS = true; ! });
  • 50. NAMING CONVENTIONS
  • 51. CONVENTIONS
  • 52. NAMING CONVENTIONS
  • 53. CONVENTIONS
  • 54. THE EMBER OBJECT MODEL
  • 55. YUI2 76 YAHOO.namespace(‘App’); ! App.BaseClass = function(){ method: function(){} }; ! App.Class = function(){ App.BaseClass.call(this); }; ! App.Class.inherits(App.BaseClass); ! App.Class.prototype.method = function(){ ! }; 2007
  • 56. var object = new Base; ! object.extend({ value: "some data”, ! method: function() { alert("Hello World!"); } ! }); ! object.method(); ! // ==> Hello World! BASE2 77 2007
  • 57. EMBER OBJECT 78 App.DefaultPlayer = Em.Object.extend({ ! init: function () { this.set('imgProfilePrefix', 'default_'); }, ! name: “Steve", ! imgName: function (imgType) { return this.get('imgProfilePrefix') + this.get('name').split(' ').join('_').toLowerCase() + this.get('imgProfileSuffix') + '.' + imgType; ! } ! });
  • 58. GETTERS AND SETTERS 79 App.DefaultPlayer = Em.Object.extend({ name: “Steve" }); ! var steve = App.DefaultPlayer.create({}); ! steve.set(‘name’ , ‘Stephen’); ! steve.get(‘name’); // Stephen
  • 59. INHERITANCE 80 App.DefaultPlayer = Em.Object.extend({}); ! App.Player = App.DefaultPlayer.extend({ … });
  • 60. SUPER() 81 App.DefaultPlayer = Em.Object.extend({ init: function () { this.set('imgProfilePrefix', 'default_'); this.set('imgProfileSuffix', '_profile'); } }); ! App.Player = App.DefaultPlayer.extend({ init: function () { this._super(); this.set('imgProfilePrefix', 'player_'); } });
  • 61. COMPUTED PROPERTIES 82 App.DefaultPlayer = Ember.Object.extend({ … name: "Steve", baseDir: "/images", imgName: function(){ return this.get('imgProfilePrefix') + this.get('name').split(' ').join('_').toLowerCase() + this.get('imgProfileSuffix') + '.png'; }, imgPath: function(){ return this.get('baseDir') + '/profile/' + this.imgName(); }.property('baseDir', 'imgName') });
  • 62. OBSERVERS 83 App.DefaultPlayer = Ember.Object.extend({ … onlineStatusChanged: function(){ console.log('onlineStatusChanged to: ' + this.get('isOnline')); }.observes('isOnline').on('init') }); ! var steve = App.DefaultPlayer.create({ 'isOnline': true }); // onlineStatusChanged to true ! steve.set('isOnline', false); // onlineStatusChanged to false
  • 63. BINDINGS 84 App.DefaultPlayer = Ember.Object.extend({ … name: "Steve", baseDir: "/images", imgName: function(){ return this.get('imgProfilePrefix') + this.get('name').split(' ').join('_').toLowerCase() + this.get('imgProfileSuffix') + '.png'; }, imgPath: function(){ return this.get('baseDir') + '/profile/' + this.imgName(); }.property('baseDir', 'imgName') });
  • 64. EMBER RUN LOOP || BACKBURNER.JS
  • 65. POST LOADED 86 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates Gadget App 1 iFrame Gadget App 2 iFrame Gadget App 3 iFrame REQ PARAMs Iframe Refresh 2007
  • 66. POST LOADED 87 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates { action:”moveGadget", version:1, category:category, order:order }; XHR REQUEST -JSON ACTION OBJECT Gadget App 1 iFrame Gadget App 2 iFrame Gadget App 3 iFrame x REQ PARAMs Iframe Refresh DRAG N’ DROP EVENT FIRES! 2007
  • 67. POST LOADED 88 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates { action:”moveGadget", version:1, category:category, order:order }; XHR REQUEST -JSON ACTION OBJECT Gadget App 1 iFrame Gadget App 2 iFrame Gadget App 3 iFrame x REQ PARAMs Iframe Refresh x { action:”moveGadget", version:1, category:category, order:order }; XHR REQUEST -JSON ACTION OBJECT 2007
  • 68. RUN LOOP 89 ! BBone.DisplayView = Backbone.View.extend({ ! initialize: function () { this.listenTo(this.model, 'change', this.render); }, render: function() { console.log(‘render’); } }); ! // render model.set('firstName', 'Erik'); // render again model.set('lastName', 'Bryn'); ! !
  • 69. ACTIONS ARE DEFERRED !
  • 70. RUN LOOP 91 ! BBone.DisplayView = Backbone.View.extend({ initialize: function () { this.listenTo(this.model, 'change', this.render); }, render: function() { backburner.deferOnce('render', this, this.actuallyRender); }, actuallyRender: function() { // do our DOM manipulations here. will only be called once. } }); backburner.run(function() { model.set('firstName', 'Erik'); model.set('lastName', 'Bryn'); }); !
  • 71. ! EMBER.RUN.QUEUES ! FLUSHING ROUTER TRANSITIONS ! ["SYNC", “ACTIONS", "ROUTERTRANSITIONS", "RENDER", "AFTERRENDER", "DESTROY"]
  • 72. ! EMBER.RUN.QUEUES ! ["SYNC", "ACTIONS", "ROUTERTRANSITIONS", "RENDER", "AFTERRENDER", "DESTROY"]
  • 73. AINT’ THAT FANCY!
  • 74. JS TEMPLATES
  • 75. DOM 96 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates JSON MARKUP LANGUAGE Main.createNode: function(type, id, classNames) { var node = document.createElement(type); node.id = id; if (typeof classNames === 'string' ) { node.className = classNames; } else if (typeof classNames === 'object' ){ var str = classNames.toString(); var classString=str.replace(/,/g,' '); node.className = classString; } return node; } 2007
  • 76. DOM VS INNERHTML 97 <script type="text/x-handlebars" data-template- name=“application"> ! <!-- template code here --> ! </script> 2007
  • 77. DOM VS INNERHTML 98 2008
  • 78. INNERHTML 99 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates gadgets.Gadget.prototype.render = function(chrome) { if (chrome) { this.getContent(function(content) { chrome.innerHTML = content; }); } }; ! 2007 JSON MARKUP LANGUAGE
  • 79. JS TEMPLATES WITH HANDLEBARS ! Ember.TEMPLATES
  • 80. HANDLEBARS 101 <div {{bind-attr class=“myClass"}}> {{myValue}} </div> Compiled JS Functions ! Em.TEMPLATES Handlebars Compiler Emits String !     //This  is  how  handlebars  works   var  output  =  "";   output.push("<div  class="");   output.push("<script  type='text/x-­‐placeholder'  id='start-­‐1'></script>");   //  insert  the  value  of  myClass   output.push("<script  type='text/x-­‐placeholder'  id='end-­‐1'></script>");   output.push("">");   output.push("<script  type='text/x-­‐placeholder'  id='start-­‐2'></script>");   //  insert  the  value  of  myValue   output.push("<script  type='text/x-­‐placeholder'  id='end-­‐2'></script>");   output.push("</div>"); Output string innerHTML
  • 81. HANDLEBARS 102 <script type="text/x-handlebars" data-template- name=“application"> <!-- template code here --> </script> grunt.initConfig({ yeoman: yeomanConfig, watch: { emberTemplates: { files: '<%= yeoman.app %>/templates/**/*.hbs', tasks: ['emberTemplates', 'livereload'] } } });
  • 82. VARIABLES 103 {{App.applicationName}} <ul class="navbar artists”> {{#each item in navbar-items}} {{#if item.isAccessible}} {{#each label in items.labels}} <li> {{#linkTo ‘label’ label.id}}{{label.title}}{{/linkTo}} </li> {{/each}} {{else}} <li> <a {{bind-attr href=‘item.fullAddress’}}> {{item.name}}</a> </li> {{/if}} {{/each}} </ul>
  • 83. MINIMAL LOGIC 104 {{App.applicationName}} <ul class="navbar artists”> {{#each item in navbar-items}} {{#if item.isAccessible}} {{#each label in items.labels}} <li> {{#linkTo ‘label’ label.id}}{{label.title}}{{/linkTo}} </li> {{/each}} {{else}} <li> <a {{bind-attr href=‘item.fullAddress’}}> {{item.name}}</a> </li> {{/if}} {{/each}} </ul>
  • 84. LINKS 105 {{App.applicationName}} <ul class="navbar artists”> {{#each item in navbar-items}} {{#if item.isAccessible}} {{#each label in items.labels}} <li> {{#linkTo ‘label’ label.id}}{{label.title}}{{/linkTo}} </li> {{/each}} {{else}} <li> <a {{bind-attr href=‘item.fullAddress’}}> {{item.name}}</a> </li> {{/if}} {{/each}} </ul>
  • 85. LISTS 106 {{App.applicationName}} <ul class="navbar artists”> {{#each item in navbar-items}} {{#if item.isAccessible}} {{#each label in item.labels}} <li> {{#linkTo ‘label’ label.id}}{{label.title}}{{/linkTo}} </li> {{/each}} {{else}} <li> <a {{bind-attr href=‘item.fullAddress’}}> {{item.name}}</a> </li> {{/if}} {{/each}} </ul>
  • 86. BOUND ATTRIBUTES 107 {{App.applicationName}} <ul class="navbar artists”> {{#each item in navbar-items}} {{#if item.isAccessible}} {{#each label in items.labels}} <li> {{#linkTo ‘label’ label.id}}{{label.title}}{{/linkTo}} </li> {{/each}} {{else}} <li> <a {{bind-attr href=‘item.fullAddress’}}> {{item.name}}</a> </li> {{/if}} {{/each}} </ul>
  • 87. ! HTMLBARS
  • 88. ! ! HTMLBARS OVER HANDLEBARS PERFORMANCE ! BIND-ATTR GONE ! METAMORPH GONE ! LOGIC IN TEMPLATES ! !
  • 89. HTMLBARS 110 <div   class=“{{myClass}}”>   {{myValue}}   </div> Compiled JS Functions ! Em.TEMPLATES HTMLBars Compiler Emits DOM elements var  output  =  dom.createDocumentFragment();   var  div  =  dom.createElement('div');   dom.RESOLVE_ATTR(context,  div,  'class',  'myClass');   var  text  =  dom.createTextNode();   dom.RESOLVE(context,  text,  'textContent',  'myValue');   div.appendChild(text);   output.appendChild(div); <div  class="{{myClass}}">{{myValue}}</div>
  • 90. BOUND ATTRIBUTES 111 {{App.applicationName}} <ul class="navbar artists”> {{#each item in navbar-items}} {{#if item.isAccessible}} {{#each label in items.labels}} <li> {{#linkTo ‘label’ label.id}}{{label.title}}{{/linkTo}} </li> {{/each}} {{else}} <li> <a {{bind-attr href=‘item.fullAddress’}}> {{item.name}}</a> </li> {{/if}} {{/each}} </ul>
  • 91. NO MORE BIND-ATTR 112 {{App.applicationName}} <ul class="navbar artists”> {{#each item in navbar-items}} {{#if item.isAccessible}} {{#each label in items.labels}} <li> {{#linkTo ‘label’ label.id}}{{label.title}}{{/linkTo}} </li> {{/each}} {{else}} <li> <a href=“{{fullAddress}}”> {{item.name}}</a> </li> {{/if}} {{/each}} </ul>
  • 92. LOGIC-LESS 113 {{App.applicationName}} <ul class="navbar artists”> {{#each item in navbar-items}} {{#if item.isAccessible}} {{#each label in items.labels}} <li> {{#linkTo ‘label’ label.id}}{{label.title}}{{/linkTo}} </li> {{/each}} {{else}} <li> <a href=“{{fullAddress}}”> {{item.name}}</a> </li> {{/if}} {{/each}} </ul>
  • 93. LOGIC 114 {{App.applicationName}} <ul class="navbar artists”> {{#each item in navbar-items}} {{#if (item.type === ‘sidenavbar-item’) }} {{#each label in items.labels}} <li> {{#linkTo ‘label’ label.id}}{{label.title}}{{/linkTo}} </li> {{/each}} {{else}} <li> <a href=“{{fullAddress}}”> {{item.name}}</a> </li> {{/if}} {{/each}} </ul>
  • 94. HTMLBARS 115 <script type="text/x-handlebars" data-template- name=“application"> ! <!-- template code here --> ! </script>
  • 95. METAMORPHS 116
  • 96. PROMISES AND THE ASYNC ROUTER
  • 97. ! ! RSVP PROMISES/A+ ! ES6 COMPLIANT ! CONVENIENCE METHODS ! ! ! ! ! !
  • 98. RSVP 120 var p = new RSVP.Promise(function(resolve, reject) { // succeed resolve(value); // or reject reject(error); }); ! p.then(function(value) { // success }, function(value) { // failure });
  • 99. CONTINUATION 121 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates ! gadgets.Gadget.prototype.getContent = function(continuation) { gadgets.callAsyncAndJoin( [this.getTitleBarContent, this.getUserPrefsDialogContent, this.getMainContent], function(results) {continuation(results.join(''));}, this); }; ! gadgets.Gadget.prototype.render = function(chrome) { if (chrome) { this.getContent(function(content) { chrome.innerHTML = content; }); } }; ! 2007
  • 100. CONTINUATION 122 ! gadgets.Gadget.prototype.getContent = function(continuation) { gadgets.callAsyncAndJoin( [this.getTitleBarContent, this.getUserPrefsDialogContent, this.getMainContent], function(results) {continuation(results.join(''));}, this); }; ! gadgets.Gadget.prototype.render = function(chrome) { if (chrome) { this.getContent(function(content) { chrome.innerHTML = content; }); } }; ! 2007
  • 101. XHR RESPONSE 123 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates { "root": “#portal", "childrenType": "Portal", "type": "ClientCommandObject", "children": [{ "childrenType": "TabContainer", "type": "Portal", "children": [{ "childrenType": "TabPage", "type": "TabContainer", "children": [{ "children": [{ "childrenType": "Gadget", "type": "Column", "children": [{ "gadgetType": "MyAccounts", "gadgetContentType": { "type": "url" } }, { "gadgetType": "EntOffersMlp", "gadgetContentType": { "type": "url" } }, { "gadgetType": "SpendingPlan", "gadgetContentType": { "type": "url" } }, { "gadgetType": "ImcoStorefront", "gadgetContentType": { "type": "url" } }, XHR RESPONSE -JSON MARKUP FORMAT Gadget App 1 iFrame Gadget App 2 iFrame Gadget App 3 iFrame x REQ PARAMs Iframe Refresh 2007
  • 102. SUCCESS, FAILURE 124 Main.YUIConnectionManager.callback = { success: function(o) { try { var data = YAHOO.lang.JSON.parse(o.responseText); } catch (e) { Main.debug(err + " - Invalid data”); } }, failure: function(o) { } }; 2007
  • 103. ROUTE HANDLERS 125 App.ArtistRoute = Ember.Route.extend({ model: function(params) { ! XHR( "some URL” , {"id":params.enid}, function callback(response){ // handle response }); ! } });
  • 104. PROMISES 126 RSVP.all([ afunction(), another(), yetAnother()]) ! .then(function() { ! console.log("They're all finished, success is ours!”); ! }, function() { ! console.error("One or more FAILED!”); });
  • 105. PROMISES 127 var promises = { posts: getJSON("/posts.json"), users: getJSON("/users.json") }; ! RSVP.hash(promises).then(function(results) { console.log(results.users) // print the users.json results console.log(results.posts) // print the posts.json results });
  • 106. PRESENT AND FUTURE ! DESIGN WITH CODE ! WORK FRONT TO BACK ! THE EMBER WAY ! THE EMBER OBJECT MODEL ! EMBER RUN LOOP AND BACKBURNER ! JS TEMPLATES W/ HANDLEBARS / HTMLBARS ! PROMISES AND THE ASYNC ROUTER ! WEB COMPONENTS ! ! ! ! ! ! !
  • 107. WEB COMPONENTS
  • 108. IFRAMES ! TRADITIONAL WEB DEVELOPERS CAN ADD CONTENT ! SANDBOXED CONTENT / CAN LOAD FROM PROXIES ! POST MESSAGE API HAS EVOLVED / CONTAINER CAN CREAT AN INTERFACE ! DONT NEED TO LEARN CONTAINER IMPLEMENTATION ! ! ! ! ! ! ! ! ! ! ! ! !
  • 109. THAT’S NASTY
  • 110. OH WAIT, ONE MORE THING. ! - ERIK BRYN, EMBER CONF 2014
  • 111. THE FUTURE IS NOW