8/6/14
THE ART AND
SCIENCE OF
SHIPPING
SINGLE PAGE
APPS WITH
EMBER.JS
UX DEVELOPMENT at frog
@jdcravens
github.com/jessecravens
jessecravens.com
principal web architect | frog Austin
ART & SCIENCE
THE ART &
SCIENCE OF
SHIPPING SINGLE
PAGE APPS WITH
EMBER.JS
2006
2007
!
WHY?
!
!
“YOU HAVE TO KNOW THE PAST TO UNDERSTAND THE PRESENT.”
!
- DR. CARL SAGAN
!
"STUDY THE PAST IF YOU WOULD DEFINE...
TEMPLATES
TRADITIONAL
14
ROUTER
UI
HTML5/CSS3/JavaScript
Web Framework
HTML5/CSS3/JavaScript
Data Services
JSON/XML
Storag...
App Server
EARLY SPA
IMPLEMENTATION
15
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Data Services
JSON/XML...
BACK IN THE DAY … A NAIVE IMPLEMENTATION
!
!
SEGREGATED DESIGN TEAM HANDED OFF PHOTOSHOP COMPS
!
ARCHITECTED SERVER IMPLEM...
App Server
INITIAL LOAD
19
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Data Services
JSON/XML
Render Engi...
INITIAL LOAD
20
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Render Engine
JavaScript DOM creation
&& Stri...
INITIAL LOAD
21
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Render Engine
JavaScript DOM creation
&& Stri...
INITIAL LOAD
22
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Render Engine
JavaScript DOM creation
&& Stri...
INITIAL LOAD
23
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Render Engine
JavaScript DOM creation
&& Stri...
INITIAL LOAD
24
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Render Engine
JavaScript DOM creation
&& Stri...
INITIAL LOAD
25
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Render Engine
JavaScript DOM creation
&& Stri...
App Server
LOADED
26
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Data Services
JSON/XML
Render Engine
Jav...
POST LOADED
27
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Render Engine
JavaScript DOM creation
&& Strin...
POST LOADED
28
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Render Engine
JavaScript DOM creation
&& Strin...
POST LOADED
29
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Render Engine
JavaScript DOM creation
&& Strin...
POST LOADED
30
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Render Engine
JavaScript DOM creation
&& Strin...
REFRESH
31
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Render Engine
JavaScript DOM creation
&& String te...
App Server
LOADED
32
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Data Services
JSON/XML
Render Engine
Jav...
WE REALLY NEED RAILS
IN THE BROWSER!
2014
EMBER
37
Views
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript Ember.App
JavaScript
Ember Data
JavaScript
ROUTER
JavaScript...
NxGEN: THE NEW S&P CAPITAL IQ
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...
BACK IN THE DAY … A NAIVE IMPLEMENTATION
!
!
SEGREGATED DESIGN TEAM HANDED OFF PHOTOSHOP COMPS
!
ARCHITECTED SERVER IMPLEM...
PRESENT AND FUTURE
!
DESIGN WITH CODE
!
WORK FRONT TO BACK
!
THE EMBER WAY
!
THE EMBER OBJECT MODEL
!
EMBER RUN LOOP AND B...
DESIGN WITH CODE
SKETCH
ADOBE EDGE REFLOW
WEBFLOW
MACAW
!
SKETCH
ADOBE EDGE REFLOW
WEBFLOW
MACAW
!
CHROME DEV TOOLS
!
WORK FRONT TO BACK
BACK END TEAM
RAPID DEV
57
Early Design: HTML, Diagram Controllers
URL Driven: State Manager and Routes First
Populate the...
RAPID DEV
REST ADAPTER
TEMPLATES
FIXTURE ADAPTER
ROUTER
ROUTE HANDLERS
CONTROLLERS
VIEWS/COMPONENTS
MODELS
SERVICES (API D...
EMBER DATA
ADAPTERS
!
ADAPTERS
60
App.ApplicationAdapter = DS.FixtureAdapter.extend({
namespace: 'rocknrollcall'
});
!
App.ApplicationAdapter = ...
NxGEN: THE NEW S&P CAPITAL IQ
API STUBS
!
EMBER APP KIT
63
{
"name": "app-kit",
"namespace": "appkit",
"APIMethod": "stub",
…
ROUTES.JS
64
module.exports = function(server) {
!
// Create an API namespace, so that the root does not
// have to be rep...
EMBER APP KIT
65
{
"name": "app-kit",
"namespace": "appkit",
"APIMethod": "stub",
…
!
{
"name": "app-kit",
"namespace": "a...
EMBRACE THE EMBER WAY
!
!
FRIENDS OR FOES
ACTIVE GENERATION
!
NAMING CONVENTIONS
!
!
ACTIVE GENERATION
EMBER APPLICATION
70
App = Ember.Application.create({
!
ENV.LOG_MODULE_RESOLVER = true;
ENV.APP.LOG_RESOLVER = true;
ENV.A...
NAMING CONVENTIONS
CONVENTIONS
NAMING CONVENTIONS
CONVENTIONS
THE EMBER OBJECT MODEL
YUI2
76
YAHOO.namespace(‘App’);
!
App.BaseClass = function(){
method: function(){}
};
!
App.Class = function(){
App.BaseCl...
var object = new Base;
!
object.extend({
value: "some data”,
!
method: function() {
alert("Hello World!");
}
!
});
!
objec...
EMBER OBJECT
78
App.DefaultPlayer = Em.Object.extend({
!
init: function () {
this.set('imgProfilePrefix', 'default_');
},
!
...
GETTERS AND SETTERS
79
App.DefaultPlayer = Em.Object.extend({
name: “Steve"
});
!
var steve = App.DefaultPlayer.create({})...
INHERITANCE
80
App.DefaultPlayer = Em.Object.extend({});
!
App.Player = App.DefaultPlayer.extend({
…
});
SUPER()
81
App.DefaultPlayer = Em.Object.extend({
init: function () {
this.set('imgProfilePrefix', 'default_');
this.set('im...
COMPUTED PROPERTIES
82
App.DefaultPlayer = Ember.Object.extend({
…
name: "Steve",
baseDir: "/images",
imgName: function(){...
OBSERVERS
83
App.DefaultPlayer = Ember.Object.extend({
…
onlineStatusChanged: function(){
console.log('onlineStatusChanged...
BINDINGS
84
App.DefaultPlayer = Ember.Object.extend({
…
name: "Steve",
baseDir: "/images",
imgName: function(){
return thi...
EMBER RUN LOOP ||
BACKBURNER.JS
POST LOADED
86
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Render Engine
JavaScript DOM creation
&& Strin...
POST LOADED
87
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Render Engine
JavaScript DOM creation
&& Strin...
POST LOADED
88
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Render Engine
JavaScript DOM creation
&& Strin...
RUN LOOP
89
!
BBone.DisplayView = Backbone.View.extend({
!
initialize: function () {
this.listenTo(this.model, 'change', t...
ACTIONS ARE DEFERRED
!
RUN LOOP
91
!
BBone.DisplayView = Backbone.View.extend({
initialize: function () {
this.listenTo(this.model, 'change', thi...
!
EMBER.RUN.QUEUES
!
FLUSHING ROUTER TRANSITIONS
!
["SYNC", “ACTIONS", "ROUTERTRANSITIONS", "RENDER", "AFTERRENDER", "DEST...
!
EMBER.RUN.QUEUES
!
["SYNC", "ACTIONS",
"ROUTERTRANSITIONS", "RENDER",
"AFTERRENDER", "DESTROY"]
AINT’ THAT
FANCY!
JS TEMPLATES
DOM
96
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Render Engine
JavaScript DOM creation
&& String templa...
DOM VS INNERHTML
97
<script type="text/x-handlebars" data-template-
name=“application">
!
<!-- template code here -->
!
</...
DOM VS INNERHTML
98
2008
INNERHTML
99
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Render Engine
JavaScript DOM creation
&& String ...
JS TEMPLATES WITH HANDLEBARS
!
Ember.TEMPLATES
HANDLEBARS
101
<div {{bind-attr
class=“myClass"}}>
{{myValue}}
</div>
Compiled JS
Functions
!
Em.TEMPLATES
Handlebars
Comp...
HANDLEBARS
102
<script type="text/x-handlebars" data-template-
name=“application">
<!-- template code here -->
</script>
g...
VARIABLES
103
{{App.applicationName}}
<ul class="navbar artists”>
{{#each item in navbar-items}}
{{#if item.isAccessible}}...
MINIMAL LOGIC
104
{{App.applicationName}}
<ul class="navbar artists”>
{{#each item in navbar-items}}
{{#if item.isAccessib...
LINKS
105
{{App.applicationName}}
<ul class="navbar artists”>
{{#each item in navbar-items}}
{{#if item.isAccessible}}
{{#...
LISTS
106
{{App.applicationName}}
<ul class="navbar artists”>
{{#each item in navbar-items}}
{{#if item.isAccessible}}
{{#...
BOUND ATTRIBUTES
107
{{App.applicationName}}
<ul class="navbar artists”>
{{#each item in navbar-items}}
{{#if item.isAcces...
!
HTMLBARS
!
!
HTMLBARS OVER HANDLEBARS
PERFORMANCE
!
BIND-ATTR GONE
!
METAMORPH GONE
!
LOGIC IN TEMPLATES
!
!
HTMLBARS
110
<div	
  
class=“{{myClass}}”>	
  
{{myValue}}	
  
</div>
Compiled JS
Functions
!
Em.TEMPLATES
HTMLBars Compil...
BOUND ATTRIBUTES
111
{{App.applicationName}}
<ul class="navbar artists”>
{{#each item in navbar-items}}
{{#if item.isAcces...
NO MORE BIND-ATTR
112
{{App.applicationName}}
<ul class="navbar artists”>
{{#each item in navbar-items}}
{{#if item.isAcce...
LOGIC-LESS
113
{{App.applicationName}}
<ul class="navbar artists”>
{{#each item in navbar-items}}
{{#if item.isAccessible}...
LOGIC
114
{{App.applicationName}}
<ul class="navbar artists”>
{{#each item in navbar-items}}
{{#if (item.type === ‘sidenav...
HTMLBARS
115
<script type="text/x-handlebars" data-template-
name=“application">
!
<!-- template code here -->
!
</script>
METAMORPHS
116
PROMISES AND THE ASYNC
ROUTER
!
!
RSVP
PROMISES/A+
!
ES6 COMPLIANT
!
CONVENIENCE METHODS
!
!
!
!
!
!
RSVP
120
var p = new RSVP.Promise(function(resolve, reject) {
// succeed
resolve(value);
// or reject
reject(error);
});
!...
CONTINUATION
121
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Render Engine
JavaScript DOM creation
&& Str...
CONTINUATION
122
!
gadgets.Gadget.prototype.getContent = function(continuation) {
gadgets.callAsyncAndJoin(
[this.getTitle...
XHR RESPONSE
123
Container App
HTML5/CSS3
Web Framework
HTML5/CSS3/JavaScript
Render Engine
JavaScript DOM creation
&& Str...
SUCCESS, FAILURE
124
Main.YUIConnectionManager.callback = {
success: function(o) {
try {
var data = YAHOO.lang.JSON.parse(...
ROUTE HANDLERS
125
App.ArtistRoute = Ember.Route.extend({
model: function(params) {
!
XHR( "some URL” , {"id":params.enid}...
PROMISES
126
RSVP.all([ afunction(), another(), yetAnother()])
!
.then(function() {
!
console.log("They're all finished, su...
PROMISES
127
var promises = {
posts: getJSON("/posts.json"),
users: getJSON("/users.json")
};
!
RSVP.hash(promises).then(f...
PRESENT AND FUTURE
!
DESIGN WITH CODE
!
WORK FRONT TO BACK
!
THE EMBER WAY
!
THE EMBER OBJECT MODEL
!
EMBER RUN LOOP AND B...
WEB COMPONENTS
IFRAMES
!
TRADITIONAL WEB DEVELOPERS CAN ADD CONTENT
!
SANDBOXED CONTENT / CAN LOAD FROM PROXIES
!
POST MESSAGE API HAS EV...
THAT’S NASTY
OH WAIT, ONE MORE THING.
!
- ERIK BRYN, EMBER CONF 2014
THE FUTURE IS NOW
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
The Art and Science of Shipping Ember Apps
Upcoming SlideShare
Loading in...5
×

The Art and Science of Shipping Ember Apps

328

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

Published in: Software
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
328
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
9
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

The Art and Science of Shipping Ember Apps

  1. 1. 8/6/14 THE ART AND SCIENCE OF SHIPPING SINGLE PAGE APPS WITH EMBER.JS UX DEVELOPMENT at frog
  2. 2. @jdcravens github.com/jessecravens jessecravens.com principal web architect | frog Austin
  3. 3. ART & SCIENCE
  4. 4. THE ART & SCIENCE OF SHIPPING SINGLE PAGE APPS WITH EMBER.JS
  5. 5. 2006
  6. 6. 2007
  7. 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. 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. 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. 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. 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. 12. INITIAL LOAD 20 Container App HTML5/CSS3 Web Framework HTML5/CSS3/JavaScript Render Engine JavaScript DOM creation && String templates initial load
  13. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 25. WE REALLY NEED RAILS IN THE BROWSER!
  26. 26. 2014
  27. 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. 28. NxGEN: THE NEW S&P CAPITAL IQ
  29. 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. 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. 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. 32. DESIGN WITH CODE
  33. 33. SKETCH ADOBE EDGE REFLOW WEBFLOW MACAW !
  34. 34. SKETCH ADOBE EDGE REFLOW WEBFLOW MACAW !
  35. 35. CHROME DEV TOOLS !
  36. 36. WORK FRONT TO BACK
  37. 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. 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. 39. EMBER DATA ADAPTERS !
  40. 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. 41. NxGEN: THE NEW S&P CAPITAL IQ
  42. 42. API STUBS !
  43. 43. EMBER APP KIT 63 { "name": "app-kit", "namespace": "appkit", "APIMethod": "stub", …
  44. 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. 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. 46. EMBRACE THE EMBER WAY
  47. 47. ! ! FRIENDS OR FOES ACTIVE GENERATION ! NAMING CONVENTIONS ! !
  48. 48. ACTIVE GENERATION
  49. 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. 50. NAMING CONVENTIONS
  51. 51. CONVENTIONS
  52. 52. NAMING CONVENTIONS
  53. 53. CONVENTIONS
  54. 54. THE EMBER OBJECT MODEL
  55. 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. 56. var object = new Base; ! object.extend({ value: "some data”, ! method: function() { alert("Hello World!"); } ! }); ! object.method(); ! // ==> Hello World! BASE2 77 2007
  57. 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. 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. 59. INHERITANCE 80 App.DefaultPlayer = Em.Object.extend({}); ! App.Player = App.DefaultPlayer.extend({ … });
  60. 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. 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. 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. 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. 64. EMBER RUN LOOP || BACKBURNER.JS
  65. 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. 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. 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. 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. 69. ACTIONS ARE DEFERRED !
  70. 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. 71. ! EMBER.RUN.QUEUES ! FLUSHING ROUTER TRANSITIONS ! ["SYNC", “ACTIONS", "ROUTERTRANSITIONS", "RENDER", "AFTERRENDER", "DESTROY"]
  72. 72. ! EMBER.RUN.QUEUES ! ["SYNC", "ACTIONS", "ROUTERTRANSITIONS", "RENDER", "AFTERRENDER", "DESTROY"]
  73. 73. AINT’ THAT FANCY!
  74. 74. JS TEMPLATES
  75. 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. 76. DOM VS INNERHTML 97 <script type="text/x-handlebars" data-template- name=“application"> ! <!-- template code here --> ! </script> 2007
  77. 77. DOM VS INNERHTML 98 2008
  78. 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. 79. JS TEMPLATES WITH HANDLEBARS ! Ember.TEMPLATES
  80. 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. 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. 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. 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. 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. 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. 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. 87. ! HTMLBARS
  88. 88. ! ! HTMLBARS OVER HANDLEBARS PERFORMANCE ! BIND-ATTR GONE ! METAMORPH GONE ! LOGIC IN TEMPLATES ! !
  89. 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. 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. 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. 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. 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. 94. HTMLBARS 115 <script type="text/x-handlebars" data-template- name=“application"> ! <!-- template code here --> ! </script>
  95. 95. METAMORPHS 116
  96. 96. PROMISES AND THE ASYNC ROUTER
  97. 97. ! ! RSVP PROMISES/A+ ! ES6 COMPLIANT ! CONVENIENCE METHODS ! ! ! ! ! !
  98. 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. 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. 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. 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. 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. 103. ROUTE HANDLERS 125 App.ArtistRoute = Ember.Route.extend({ model: function(params) { ! XHR( "some URL” , {"id":params.enid}, function callback(response){ // handle response }); ! } });
  104. 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. 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. 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. 107. WEB COMPONENTS
  108. 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. 109. THAT’S NASTY
  110. 110. OH WAIT, ONE MORE THING. ! - ERIK BRYN, EMBER CONF 2014
  111. 111. THE FUTURE IS NOW
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×