Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Art and Science of Shipping Single Page App...

The Art and Science of Shipping Single Page Apps with EmberJS

Jesse Cravens is a principal web engineer at frog Austin and a technical author for O’Reilly Media. As early as 2005, Jesse began to focus on JavaScript and the evolution of the web browser as an application platform. In 2007, a contract position with a large, insurance company began a journey of leading projects with a focus on complex UIs in Fortune 200 enterprises. Throughout the years, he has tackled some of the most common challenges presented by implementing ambitious web UIs in enterprise environments: lack of JavaScript and front-end expertise, server-centric web legacies, cross-browser and mobile-web fragmentation compatibility requirements, lack of development tooling for front end developers, and the nascency, or absence, of client MVC toolsets, binding libraries, client side ORMs, JavaScript templates, promises, and web components. Through these experiences, and along side the maturation of the technical options, he has developed a methodology that balances the art and science of pushing SPAs into production that uses Ember.js as the primary toolset. Join Jesse on a trip from the past, to now, and into the future of browser-centric, web application development.

Jesse Cravens

August 07, 2014
Tweet

More Decks by Jesse Cravens

Other Decks in Technology

Transcript

  1. 8/6/14 THE ART AND SCIENCE OF SHIPPING SINGLE PAGE APPS

    WITH EMBER.JS UX DEVELOPMENT at frog
  2. ! WHY? ! ! “YOU HAVE TO KNOW THE PAST

    TO UNDERSTAND THE PRESENT.” ! - DR. CARL SAGAN ! "STUDY THE PAST IF YOU WOULD DEFINE THE FUTURE...." ! - CONFUCIUS ! ! ! ! !
  3. 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
  4. 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
  5. 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 ! ! ! ! ! ! !
  6. 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
  7. INITIAL LOAD 20 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render

    Engine JavaScript DOM creation && String templates initial load
  8. 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" } },
  9. 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);
  10. 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]); } } };
  11. 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; }
  12. 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; }); } }; !
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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.
  20. 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
  21. 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.
  22. 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 ! ! ! ! ! ! !
  23. 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 ! ! ! ! ! ! !
  24. 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
  25. 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
  26. 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' });
  27. 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); }); }); };
  28. EMBER APP KIT 65 { "name": "app-kit", "namespace": "appkit", "APIMethod":

    "stub", … ! { "name": "app-kit", "namespace": "appkit", "APIMethod": “proxy”, "proxyURL": "http://whatever.api:3232", ...
  29. 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; ! });
  30. 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
  31. var object = new Base; ! object.extend({ value: "some data”,

    ! method: function() { alert("Hello World!"); } ! }); ! object.method(); ! // ==> Hello World! BASE2 77 2007
  32. 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; ! } ! });
  33. GETTERS AND SETTERS 79 App.DefaultPlayer = Em.Object.extend({ name: “Steve" });

    ! var steve = App.DefaultPlayer.create({}); ! steve.set(‘name’ , ‘Stephen’); ! steve.get(‘name’); // Stephen
  34. 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_'); } });
  35. 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') });
  36. 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
  37. 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') });
  38. 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
  39. 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
  40. 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
  41. 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'); ! !
  42. 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'); }); !
  43. 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
  44. 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
  45. 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
  46. 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'] } } });
  47. 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>
  48. 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>
  49. 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>
  50. 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>
  51. 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>
  52. ! ! HTMLBARS OVER HANDLEBARS PERFORMANCE ! BIND-ATTR GONE !

    METAMORPH GONE ! LOGIC IN TEMPLATES ! !
  53. 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>
  54. 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>
  55. 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>
  56. 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>
  57. 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>
  58. 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 });
  59. 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
  60. 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
  61. 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
  62. 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
  63. ROUTE HANDLERS 125 App.ArtistRoute = Ember.Route.extend({ model: function(params) { !

    XHR( "some URL” , {"id":params.enid}, function callback(response){ // handle response }); ! } });
  64. 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!”); });
  65. 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 });
  66. 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 ! ! ! ! ! ! !
  67. 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 ! ! ! ! ! ! ! ! ! ! ! ! !