134
8/6/14 THE ART AND SCIENCE OF SHIPPING SINGLE PAGE APPS WITH EMBER.JS UX DEVELOPMENT at frog

The Art and Science of Shipping Ember Apps

Embed Size (px)

DESCRIPTION

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.

Citation preview

Page 1: The Art and Science of Shipping Ember Apps

8/6/14

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

UX DEVELOPMENT at frog

Page 2: The Art and Science of Shipping Ember Apps

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

Page 3: The Art and Science of Shipping Ember Apps
Page 4: The Art and Science of Shipping Ember Apps
Page 5: The Art and Science of Shipping Ember Apps

ART & SCIENCE

Page 6: The Art and Science of Shipping Ember Apps
Page 7: The Art and Science of Shipping Ember Apps

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

Page 8: The Art and Science of Shipping Ember Apps

2006

Page 9: The Art and Science of Shipping Ember Apps
Page 10: The Art and Science of Shipping Ember Apps

2007

Page 11: The Art and Science of Shipping Ember Apps
Page 12: The Art and Science of Shipping Ember Apps
Page 13: The Art and Science of Shipping Ember Apps

!WHY? !!“YOU HAVE TO KNOW THE PAST TO UNDERSTAND THE PRESENT.”

!- DR. CARL SAGAN

!"STUDY THE PAST IF YOU WOULD DEFINE THE FUTURE...."

!- CONFUCIUS

!!

!!

!

Page 14: The Art and Science of Shipping Ember Apps

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

Page 15: The Art and Science of Shipping Ember Apps

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

Page 16: The Art and Science of Shipping Ember Apps
Page 17: The Art and Science of Shipping Ember Apps
Page 18: The Art and Science of Shipping Ember Apps

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

!!

!

Page 19: The Art and Science of Shipping Ember Apps

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

Page 20: The Art and Science of Shipping Ember Apps

INITIAL LOAD

20

Container App HTML5/CSS3

Web Framework HTML5/CSS3/JavaScript

Render Engine JavaScript DOM creation && String templates

initial load

Page 21: The Art and Science of Shipping Ember Apps

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

Page 22: The Art and Science of Shipping Ember Apps

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

Page 23: The Art and Science of Shipping Ember Apps

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

Page 24: The Art and Science of Shipping Ember Apps

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

Page 25: The Art and Science of Shipping Ember Apps

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

Page 26: The Art and Science of Shipping Ember Apps

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

Page 27: The Art and Science of Shipping Ember Apps

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

Page 28: The Art and Science of Shipping Ember Apps

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

Page 29: The Art and Science of Shipping Ember Apps

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

Page 30: The Art and Science of Shipping Ember Apps

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

Page 31: The Art and Science of Shipping Ember Apps

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

Page 32: The Art and Science of Shipping Ember Apps

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.

Page 33: The Art and Science of Shipping Ember Apps

WE REALLY NEED RAILS IN THE BROWSER!

Page 34: The Art and Science of Shipping Ember Apps
Page 35: The Art and Science of Shipping Ember Apps
Page 36: The Art and Science of Shipping Ember Apps

2014

Page 37: The Art and Science of Shipping Ember Apps

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

Page 38: The Art and Science of Shipping Ember Apps
Page 39: The Art and Science of Shipping Ember Apps

NxGEN: THE NEW S&P CAPITAL IQ

Page 40: The Art and Science of Shipping Ember Apps
Page 41: The Art and Science of Shipping Ember Apps
Page 42: The Art and Science of Shipping Ember Apps
Page 43: The Art and Science of Shipping Ember Apps
Page 44: The Art and Science of Shipping Ember Apps
Page 45: The Art and Science of Shipping Ember Apps
Page 46: The Art and Science of Shipping Ember Apps
Page 47: The Art and Science of Shipping Ember Apps

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.

Page 48: The Art and Science of Shipping Ember Apps

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

!!

!

Page 49: The Art and Science of Shipping Ember Apps

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

!!

!

Page 50: The Art and Science of Shipping Ember Apps

DESIGN WITH CODE

Page 51: The Art and Science of Shipping Ember Apps

SKETCH ADOBE EDGE REFLOW WEBFLOW MACAW !

Page 52: The Art and Science of Shipping Ember Apps

SKETCH ADOBE EDGE REFLOW WEBFLOW MACAW !

Page 53: The Art and Science of Shipping Ember Apps

CHROME DEV TOOLS !

Page 54: The Art and Science of Shipping Ember Apps
Page 55: The Art and Science of Shipping Ember Apps

WORK FRONT TO BACK

Page 56: The Art and Science of Shipping Ember Apps
Page 57: The Art and Science of Shipping Ember Apps

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

Page 58: The Art and Science of Shipping Ember Apps

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

DEPLOY

CONCEPTUAL

Page 59: The Art and Science of Shipping Ember Apps

EMBER DATA ADAPTERS !

Page 60: The Art and Science of Shipping Ember Apps

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

Page 61: The Art and Science of Shipping Ember Apps

NxGEN: THE NEW S&P CAPITAL IQ

Page 62: The Art and Science of Shipping Ember Apps

API STUBS !

Page 63: The Art and Science of Shipping Ember Apps

EMBER APP KIT

63

{ "name": "app-kit", "namespace": "appkit", "APIMethod": "stub", …

Page 64: The Art and Science of Shipping Ember Apps

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

Page 65: The Art and Science of Shipping Ember Apps

EMBER APP KIT

65

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

Page 66: The Art and Science of Shipping Ember Apps

EMBRACE THE EMBER WAY

Page 67: The Art and Science of Shipping Ember Apps
Page 68: The Art and Science of Shipping Ember Apps

!!FRIENDS OR FOES

ACTIVE GENERATION !

NAMING CONVENTIONS !

!

Page 69: The Art and Science of Shipping Ember Apps

ACTIVE GENERATION

Page 70: The Art and Science of Shipping Ember Apps

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

Page 71: The Art and Science of Shipping Ember Apps

NAMING CONVENTIONS

Page 72: The Art and Science of Shipping Ember Apps

CONVENTIONS

Page 73: The Art and Science of Shipping Ember Apps

NAMING CONVENTIONS

Page 74: The Art and Science of Shipping Ember Apps

CONVENTIONS

Page 75: The Art and Science of Shipping Ember Apps

THE EMBER OBJECT MODEL

Page 76: The Art and Science of Shipping Ember Apps

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

Page 77: The Art and Science of Shipping Ember Apps

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

BASE2

77200

7

Page 78: The Art and Science of Shipping Ember Apps

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

Page 79: The Art and Science of Shipping Ember Apps

GETTERS AND SETTERS

79

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

Page 80: The Art and Science of Shipping Ember Apps

INHERITANCE

80

App.DefaultPlayer = Em.Object.extend({}); !App.Player = App.DefaultPlayer.extend({ … });

Page 81: The Art and Science of Shipping Ember Apps

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

Page 82: The Art and Science of Shipping Ember Apps

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

Page 83: The Art and Science of Shipping Ember Apps

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

Page 84: The Art and Science of Shipping Ember Apps

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

Page 85: The Art and Science of Shipping Ember Apps

EMBER RUN LOOP || BACKBURNER.JS

Page 86: The Art and Science of Shipping Ember Apps

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

Page 87: The Art and Science of Shipping Ember Apps

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

Page 88: The Art and Science of Shipping Ember Apps

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

Page 89: The Art and Science of Shipping Ember Apps

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

Page 90: The Art and Science of Shipping Ember Apps

ACTIONS ARE DEFERRED !

Page 91: The Art and Science of Shipping Ember Apps

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

Page 92: The Art and Science of Shipping Ember Apps

!

EMBER.RUN.QUEUES !

FLUSHING ROUTER TRANSITIONS !

["SYNC", “ACTIONS", "ROUTERTRANSITIONS", "RENDER", "AFTERRENDER", "DESTROY"]

Page 93: The Art and Science of Shipping Ember Apps

!

EMBER.RUN.QUEUES !

["SYNC", "ACTIONS", "ROUTERTRANSITIONS", "RENDER", "AFTERRENDER", "DESTROY"]

Page 94: The Art and Science of Shipping Ember Apps

AINT’ THAT FANCY!

Page 95: The Art and Science of Shipping Ember Apps

JS TEMPLATES

Page 96: The Art and Science of Shipping Ember Apps

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

Page 97: The Art and Science of Shipping Ember Apps

DOM VS INNERHTML

97

<script type="text/x-handlebars" data-template-name=“application"> ! <!-- template code here --> !</script>

2007

Page 98: The Art and Science of Shipping Ember Apps

DOM VS INNERHTML

98200

8

Page 99: The Art and Science of Shipping Ember Apps

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

Page 100: The Art and Science of Shipping Ember Apps

JS TEMPLATES WITH HANDLEBARS !

Ember.TEMPLATES

Page 101: The Art and Science of Shipping Ember Apps

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

Page 102: The Art and Science of Shipping Ember Apps

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

Page 103: The Art and Science of Shipping Ember Apps

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>

Page 104: The Art and Science of Shipping Ember Apps

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>

Page 105: The Art and Science of Shipping Ember Apps

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>

Page 106: The Art and Science of Shipping Ember Apps

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>

Page 107: The Art and Science of Shipping Ember Apps

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>

Page 108: The Art and Science of Shipping Ember Apps

!

HTMLBARS

Page 109: The Art and Science of Shipping Ember Apps

!!HTMLBARS OVER HANDLEBARS

PERFORMANCE !

BIND-ATTR GONE !

METAMORPH GONE !

LOGIC IN TEMPLATES !

!

Page 110: The Art and Science of Shipping Ember Apps

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>

Page 111: The Art and Science of Shipping Ember Apps

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>

Page 112: The Art and Science of Shipping Ember Apps

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>

Page 113: The Art and Science of Shipping Ember Apps

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>

Page 114: The Art and Science of Shipping Ember Apps

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>

Page 115: The Art and Science of Shipping Ember Apps

HTMLBARS

115

<script type="text/x-handlebars" data-template-name=“application"> ! <!-- template code here --> !</script>

Page 116: The Art and Science of Shipping Ember Apps

METAMORPHS

116

Page 117: The Art and Science of Shipping Ember Apps
Page 118: The Art and Science of Shipping Ember Apps

PROMISES AND THE ASYNC ROUTER

Page 119: The Art and Science of Shipping Ember Apps

!!RSVP

PROMISES/A+ !

ES6 COMPLIANT !

CONVENIENCE METHODS !!!!!

!

Page 120: The Art and Science of Shipping Ember Apps

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

Page 121: The Art and Science of Shipping Ember Apps

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

7

Page 122: The Art and Science of Shipping Ember Apps

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

Page 123: The Art and Science of Shipping Ember Apps

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

Page 124: The Art and Science of Shipping Ember Apps

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

Page 125: The Art and Science of Shipping Ember Apps

ROUTE HANDLERS

125

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

Page 126: The Art and Science of Shipping Ember Apps

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

Page 127: The Art and Science of Shipping Ember Apps

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

Page 128: The Art and Science of Shipping Ember Apps

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

!!

!

Page 129: The Art and Science of Shipping Ember Apps

WEB COMPONENTS

Page 130: The Art and Science of Shipping Ember Apps

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

!!

!

Page 131: The Art and Science of Shipping Ember Apps

THAT’S NASTY

Page 132: The Art and Science of Shipping Ember Apps

OH WAIT, ONE MORE THING. !

- ERIK BRYN, EMBER CONF 2014

Page 133: The Art and Science of Shipping Ember Apps

THE FUTURE IS NOW

Page 134: The Art and Science of Shipping Ember Apps