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

The Art and Science of Shipping Single Page Apps with EmberJS

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

    View full-size slide

  2. @jdcravens
    github.com/jessecravens
    jessecravens.com
    principal web architect | frog Austin

    View full-size slide

  3. ART & SCIENCE

    View full-size slide

  4. THE ART &
    SCIENCE OF
    SHIPPING SINGLE
    PAGE APPS WITH
    EMBER.JS

    View full-size slide

  5. !
    WHY?
    !
    !
    “YOU HAVE TO KNOW THE PAST TO UNDERSTAND THE PRESENT.”
    !
    - DR. CARL SAGAN
    !
    "STUDY THE PAST IF YOU WOULD DEFINE THE FUTURE...."
    !
    - CONFUCIUS
    !
    !
    !
    !
    !

    View full-size slide

  6. 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

    View full-size slide

  7. 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

    View full-size slide

  8. 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
    !
    !
    !
    !
    !
    !
    !

    View full-size slide

  9. 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

    View full-size slide

  10. INITIAL LOAD
    20
    Container App
    HTML5/CSS3
    Web Framework
    HTML5/CSS3/JavaScript
    Render Engine
    JavaScript DOM creation
    && String templates
    initial load

    View full-size slide

  11. 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"
    }
    },

    View full-size slide

  12. 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);

    View full-size slide

  13. 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]);
    }
    }
    };

    View full-size slide

  14. 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;
    }

    View full-size slide

  15. 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;
    });
    }
    };
    !

    View full-size slide

  16. 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

    View full-size slide

  17. 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

    View full-size slide

  18. 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

    View full-size slide

  19. 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

    View full-size slide

  20. 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

    View full-size slide

  21. 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

    View full-size slide

  22. 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.

    View full-size slide

  23. WE REALLY NEED RAILS
    IN THE BROWSER!

    View full-size slide

  24. 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

    View full-size slide

  25. NxGEN: THE NEW S&P CAPITAL IQ

    View full-size slide

  26. 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.

    View full-size slide

  27. 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
    !
    !
    !
    !
    !
    !
    !

    View full-size slide

  28. 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
    !
    !
    !
    !
    !
    !
    !

    View full-size slide

  29. DESIGN WITH CODE

    View full-size slide

  30. SKETCH
    ADOBE EDGE REFLOW
    WEBFLOW
    MACAW
    !

    View full-size slide

  31. SKETCH
    ADOBE EDGE REFLOW
    WEBFLOW
    MACAW
    !

    View full-size slide

  32. CHROME DEV TOOLS
    !

    View full-size slide

  33. WORK FRONT TO BACK

    View full-size slide

  34. 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

    View full-size slide

  35. 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

    View full-size slide

  36. EMBER DATA
    ADAPTERS
    !

    View full-size slide

  37. 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'
    });

    View full-size slide

  38. NxGEN: THE NEW S&P CAPITAL IQ

    View full-size slide

  39. EMBER APP KIT
    63
    {
    "name": "app-kit",
    "namespace": "appkit",
    "APIMethod": "stub",

    View full-size slide

  40. 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);
    });
    });
    };

    View full-size slide

  41. EMBER APP KIT
    65
    {
    "name": "app-kit",
    "namespace": "appkit",
    "APIMethod": "stub",

    !
    {
    "name": "app-kit",
    "namespace": "appkit",
    "APIMethod": “proxy”,
    "proxyURL": "http://whatever.api:3232",
    ...

    View full-size slide

  42. EMBRACE THE EMBER WAY

    View full-size slide

  43. !
    !
    FRIENDS OR FOES
    ACTIVE GENERATION
    !
    NAMING CONVENTIONS
    !
    !

    View full-size slide

  44. ACTIVE GENERATION

    View full-size slide

  45. 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;
    !
    });

    View full-size slide

  46. NAMING CONVENTIONS

    View full-size slide

  47. NAMING CONVENTIONS

    View full-size slide

  48. THE EMBER OBJECT MODEL

    View full-size slide

  49. 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

    View full-size slide

  50. var object = new Base;
    !
    object.extend({
    value: "some data”,
    !
    method: function() {
    alert("Hello World!");
    }
    !
    });
    !
    object.method();
    !
    // ==> Hello World!
    BASE2
    77
    2007

    View full-size slide

  51. 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;
    !
    }
    !
    });

    View full-size slide

  52. GETTERS AND SETTERS
    79
    App.DefaultPlayer = Em.Object.extend({
    name: “Steve"
    });
    !
    var steve = App.DefaultPlayer.create({});
    !
    steve.set(‘name’ , ‘Stephen’);
    !
    steve.get(‘name’); // Stephen

    View full-size slide

  53. INHERITANCE
    80
    App.DefaultPlayer = Em.Object.extend({});
    !
    App.Player = App.DefaultPlayer.extend({

    });

    View full-size slide

  54. 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_');
    }
    });

    View full-size slide

  55. 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')
    });

    View full-size slide

  56. 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

    View full-size slide

  57. 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')
    });

    View full-size slide

  58. EMBER RUN LOOP ||
    BACKBURNER.JS

    View full-size slide

  59. 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

    View full-size slide

  60. 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

    View full-size slide

  61. 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

    View full-size slide

  62. 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');
    !
    !

    View full-size slide

  63. ACTIONS ARE DEFERRED
    !

    View full-size slide

  64. 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');
    });
    !

    View full-size slide

  65. !
    EMBER.RUN.QUEUES
    !
    FLUSHING ROUTER TRANSITIONS
    !
    ["SYNC", “ACTIONS", "ROUTERTRANSITIONS", "RENDER", "AFTERRENDER", "DESTROY"]

    View full-size slide

  66. !
    EMBER.RUN.QUEUES
    !
    ["SYNC", "ACTIONS",
    "ROUTERTRANSITIONS", "RENDER",
    "AFTERRENDER", "DESTROY"]

    View full-size slide

  67. AINT’ THAT
    FANCY!

    View full-size slide

  68. JS TEMPLATES

    View full-size slide

  69. 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

    View full-size slide

  70. DOM VS INNERHTML
    97
    name=“application">
    !

    !

    2007

    View full-size slide

  71. DOM VS INNERHTML
    98
    2008

    View full-size slide

  72. 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

    View full-size slide

  73. JS TEMPLATES WITH HANDLEBARS
    !
    Ember.TEMPLATES

    View full-size slide

  74. HANDLEBARS
    101
    class=“myClass"}}>
    {{myValue}}

    Compiled JS
    Functions
    !
    Em.TEMPLATES
    Handlebars
    Compiler
    Emits
    String
    !
       
    //This  is  how  handlebars  works  
    var  output  =  "";  
    output.push("output.push("");  
    //  insert  the  value  of  myClass  
    output.push("");  
    output.push("\">");  
    output.push("");  
    //  insert  the  value  of  myValue  
    output.push("");  
    output.push("");
    Output
    string
    innerHTML

    View full-size slide

  75. HANDLEBARS
    102
    name=“application">


    grunt.initConfig({
    yeoman: yeomanConfig,
    watch: {
    emberTemplates: {
    files: '<%= yeoman.app %>/templates/**/*.hbs',
    tasks: ['emberTemplates', 'livereload']
    }
    }
    });

    View full-size slide

  76. VARIABLES
    103
    {{App.applicationName}}

    View full-size slide

  77. MINIMAL LOGIC
    104
    {{App.applicationName}}

    View full-size slide

  78. LINKS
    105
    {{App.applicationName}}

    View full-size slide

  79. LISTS
    106
    {{App.applicationName}}

    View full-size slide

  80. BOUND ATTRIBUTES
    107
    {{App.applicationName}}

    View full-size slide

  81. !
    !
    HTMLBARS OVER HANDLEBARS
    PERFORMANCE
    !
    BIND-ATTR GONE
    !
    METAMORPH GONE
    !
    LOGIC IN TEMPLATES
    !
    !

    View full-size slide

  82. HTMLBARS
    110
    class=“{{myClass}}”>  
    {{myValue}}  

    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);
    {{myValue}}

    View full-size slide

  83. BOUND ATTRIBUTES
    111
    {{App.applicationName}}

    View full-size slide

  84. NO MORE BIND-ATTR
    112
    {{App.applicationName}}

    View full-size slide

  85. LOGIC-LESS
    113
    {{App.applicationName}}

    View full-size slide

  86. LOGIC
    114
    {{App.applicationName}}

    View full-size slide

  87. HTMLBARS
    115
    name=“application">
    !

    !

    View full-size slide

  88. METAMORPHS
    116

    View full-size slide

  89. PROMISES AND THE ASYNC
    ROUTER

    View full-size slide

  90. !
    !
    RSVP
    PROMISES/A+
    !
    ES6 COMPLIANT
    !
    CONVENIENCE METHODS
    !
    !
    !
    !
    !
    !

    View full-size slide

  91. 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
    });

    View full-size slide

  92. 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

    View full-size slide

  93. 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

    View full-size slide

  94. 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

    View full-size slide

  95. 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

    View full-size slide

  96. ROUTE HANDLERS
    125
    App.ArtistRoute = Ember.Route.extend({
    model: function(params) {
    !
    XHR( "some URL” , {"id":params.enid}, function
    callback(response){
    // handle response
    });
    !
    }
    });

    View full-size slide

  97. 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!”);
    });

    View full-size slide

  98. 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
    });

    View full-size slide

  99. 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
    !
    !
    !
    !
    !
    !
    !

    View full-size slide

  100. WEB COMPONENTS

    View full-size slide

  101. 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
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !
    !

    View full-size slide

  102. THAT’S NASTY

    View full-size slide

  103. OH WAIT, ONE MORE THING.
    !
    - ERIK BRYN, EMBER CONF 2014

    View full-size slide

  104. THE FUTURE IS NOW

    View full-size slide