Complex Architectures in Ember

70
Hi. I’m Matthew. I build spiffy apps for clients in NYC Thursday, September 19, 13

description

In Ember.js, routes and templates dictate the architecture of your app. This presentation will talk about why this is, and what tools Ember provides to manage architectural complexity.

Transcript of Complex Architectures in Ember

Page 1: Complex Architectures in Ember

Hi. I’m Matthew.I build spiffy apps for clients in NYC

Thursday, September 19, 13

Page 3: Complex Architectures in Ember

Let’s go single page

Thursday, September 19, 13

Page 4: Complex Architectures in Ember

AMBITIOUS

Thursday, September 19, 13

Page 5: Complex Architectures in Ember

COMPLEXThursday, September 19, 13

Page 6: Complex Architectures in Ember

Start simple.

Thursday, September 19, 13

Page 7: Complex Architectures in Ember

title

body

preview submit

preview

Thursday, September 19, 13

Page 8: Complex Architectures in Ember

title

body

preview submit

preview

Thursday, September 19, 13

Page 9: Complex Architectures in Ember

1 <div style="clear:both"> 2 <div style="float:left"> 3 {{input value=title}} 4 {{textarea value=body}} 5 </div> 6 <div style="float:left"> 7 {{post-preview markdown=body}} 8 </div> 9 </div>10 11 <div style="clear:both">12 <button style="float:left" {{action "preview"}}>Preview</button>13 <button style="float:left" {{action "submit"}}>Submit</button>14 </div>

Thursday, September 19, 13

Page 10: Complex Architectures in Ember

Great! HTML.

Thursday, September 19, 13

Page 11: Complex Architectures in Ember

HTML dictates layout.

Thursday, September 19, 13

Page 12: Complex Architectures in Ember

HTML dictate layout.Templates

Thursday, September 19, 13

Page 13: Complex Architectures in Ember

AND YOUR ARCHITECTURE

Thursday, September 19, 13

Page 14: Complex Architectures in Ember

1 <div style="clear:both"> 2 <div style="float:left"> 3 {{input value=title}} 4 {{textarea value=body}} 5 </div> 6 <div style="float:left"> 7 {{post-preview markdown=body}} 8 </div> 9 </div>10 11 <div style="clear:both">12 <button style="float:left" {{action "preview"}}>Preview</button>13 <button style="float:left" {{action "submit"}}>Submit</button>14 </div>

ACTION DOESNT TALK TO COMPONENTS

Thursday, September 19, 13

Page 15: Complex Architectures in Ember

1 <div style="clear:both"> 2 <div style="float:left"> 3 {{input value=title}} 4 {{textarea value=body}} 5 </div> 6 <div style="float:left"> 7 {{post-preview markdown=body}} 8 </div> 9 </div>10 11 <div style="clear:both">12 <button style="float:left" {{action "preview"}}>Preview</button>13 <button style="float:left" {{action "submit"}}>Submit</button>14 </div>

CHASM OF DOM

Thursday, September 19, 13

Page 16: Complex Architectures in Ember

1 <div style="clear:both"> 2 <div style="float:left"> 3 {{input value=title}} 4 {{textarea value=body}} 5 </div> 6 <div style="float:left"> 7 {{post-preview markdown=body viewName="preview"}} 8 </div> 9 </div>10 11 <div style="clear:both">12 <button style="float:left" {{action "preview" target=view.preview}}>Preview</button>13 <button style="float:left" {{action "submit"}}>Submit</button>14 </div>

WORKAROUND 1

Thursday, September 19, 13

Page 17: Complex Architectures in Ember

1 <div style="clear:both"> 2 <div style="float:left"> 3 {{input value=title}} 4 {{textarea value=body}} 5 </div> 6 <div style="float:left"> 7 {{view App.PostPreview markdownBinding=body viewName="preview"}} 8 </div> 9 </div>10 11 <div style="clear:both">12 <button style="float:left" {{action "preview" target=view.preview}}>Preview</button>13 <button style="float:left" {{action "submit"}}>Submit</button>14 </div>

WORKAROUND 2

Thursday, September 19, 13

Page 18: Complex Architectures in Ember

1 <div style="clear:both"> 2 <div style="float:left"> 3 {{input value=title}} 4 {{textarea value=body}} 5 </div> 6 <div style="float:left"> 7 {{render "post_preview" body}} 8 </div> 9 </div>10 11 <div style="clear:both">12 <button style="float:left" {{action "preview"}}>Preview</button>13 <button style="float:left" {{action "submit"}}>Submit</button>14 </div>

1 App.ApplicationRoute = Ember.Route.extend({2 actions: {3 preview: function(){4 var controller = this.controllerFor('post_preview');5 controller.updatePreview();6 }7 }8 });

WORKAROUND 3

Thursday, September 19, 13

Page 19: Complex Architectures in Ember

WORKAROUND 4

1 App.ApplicationRoute = Ember.Route.extend({ 2 renderTemplate: function(){ 3 this._super.apply(this, arguments); 4 this.render('post_preview', { 5 into: 'application', 6 outlet: 'preview', 7 controller: 'post_preview' 8 }); 9 },10 actions: {11 preview: function(){12 var app = this.controllerFor('application');13 var preview = this.controllerFor('post_preview');14 preview.set('markdown', app.get('body'));15 }16 }17 });18 19 App.PostPreviewController = Ember.Controller.extend();

1 <div style="clear:both"> 2 <div style="float:left"> 3 {{input value=title}} 4 {{textarea value=body}} 5 </div> 6 <div style="float:left"> 7 {{outlet "preview"}} 8 </div> 9 </div>10 11 <div style="clear:both">12 <button style="float:left" {{action "preview"}}>Preview</button>13 <button style="float:left" {{action "submit"}}>Submit</button>14 </div>

Thursday, September 19, 13

Page 20: Complex Architectures in Ember

When we pick between these options...

Thursday, September 19, 13

Page 21: Complex Architectures in Ember

We make design decisions.

Thursday, September 19, 13

Page 22: Complex Architectures in Ember

• Re-usability as ui component• re-usability as action• If an action fires• Where the action is handled• The internals of preview

Thursday, September 19, 13

Page 23: Complex Architectures in Ember

ARCHITECTURE

Thursday, September 19, 13

Page 24: Complex Architectures in Ember

Architecture in Ember apps is dictated by routes and templates.

Thursday, September 19, 13

Page 25: Complex Architectures in Ember

Routes and templates decide how actions propagate the controller/route tree, scope access

to dependencies, and are most subject to external constraints.

Thursday, September 19, 13

Page 26: Complex Architectures in Ember

Routes and templates decide how actions propagate the controller/route tree, scope access

to dependencies, and are most subject to external constraints.

Thursday, September 19, 13

Page 27: Complex Architectures in Ember

Routes and templates decide how actions propagate the controller/route tree, scope access

to dependencies, and are most subject to external constraints.

Thursday, September 19, 13

Page 28: Complex Architectures in Ember

Routes and templates decide how actions propagate the controller/route tree, scope access

to dependencies, and are most subject to external constraints.

Thursday, September 19, 13

Page 29: Complex Architectures in Ember

Routes and templates decide how actions propagate the controller/route tree, scope access

to dependencies, and are most subject to external constraints.

Thursday, September 19, 13

Page 30: Complex Architectures in Ember

“Understanding actions in two easy steps”

Thursday, September 19, 13

Page 31: Complex Architectures in Ember

#1: Bubbling

Thursday, September 19, 13

Page 32: Complex Architectures in Ember

WARNING

THEse slides describe the behavior IN rc8 and beyond. behavior before the changes in rc8 is very similar to

what is described here, but not identical.

Thursday, September 19, 13

Page 33: Complex Architectures in Ember

ACTION BUBBLING

1 var Actionable = Ember.Object.extend(Ember.ActionHandler);2 3 var firstTarget = Actionable.create();4 5 firstTarget.send("Wie Geht's");6 7 // Nothing!

Thursday, September 19, 13

Page 34: Complex Architectures in Ember

ACTION BUBBLING

1 var Actionable = Ember.Object.extend(Ember.ActionHandler); 2 3 var firstTarget = Actionable.extend({ 4 actions: { 5 "Wie Geht's": function(){ 6 console.log("Danke gut"); 7 } 8 } 9 }).create();10 11 firstTarget.send("Wie Geht's");12 13 // Danke gut

Thursday, September 19, 13

Page 35: Complex Architectures in Ember

ACTION BUBBLING

1 var Actionable = Ember.Object.extend(Ember.ActionHandler); 2 3 var secondTarget = Actionable.extend({ 4 actions: { 5 "Wie Geht's": function(){ 6 console.log("Und dir?"); 7 } 8 } 9 }).create();10 11 var firstTarget = Actionable.extend({12 target: secondTarget,13 actions: {14 "Wie Geht's": function(){15 console.log("Danke gut");16 }17 }18 }).create();19 20 firstTarget.send("Wie Geht's");21 22 // Danke gut

Thursday, September 19, 13

Page 36: Complex Architectures in Ember

ACTION BUBBLING 1 var Actionable = Ember.Object.extend(Ember.ActionHandler); 2 3 var secondTarget = Actionable.extend({ 4 actions: { 5 "Wie Geht's": function(){ 6 console.log("Und dir?"); 7 } 8 } 9 }).create();10 11 var firstTarget = Actionable.extend({12 target: secondTarget,13 actions: {14 "Wie Geht's": function(){15 console.log("Danke gut");16 return true;17 }18 }19 }).create();20 21 firstTarget.send("Wie Geht's");22 23 // Danke gut24 // Und dir?

RETUrn true to continue bubbling the action

Thursday, September 19, 13

Page 37: Complex Architectures in Ember

ACTION BUBBLING

1 var Actionable = Ember.Object.extend(Ember.ActionHandler); 2 3 var secondTarget = Actionable.extend({ 4 actions: { 5 "Wie Geht's": function(){ 6 console.log("Und dir?"); 7 } 8 } 9 }).create();10 11 var firstTarget = Actionable.extend({12 target: secondTarget,13 actions: {14 "Wie Geht's": null, // Or just don't define actions15 }16 }).create();17 18 firstTarget.send("Wie Geht's");19 20 // Und dir?

Thursday, September 19, 13

Page 38: Complex Architectures in Ember

Controllers, Routes, Views, and Components handle actions.

Thursday, September 19, 13

Page 39: Complex Architectures in Ember

Only Controllers and Routes have targets for bubbling.

Thursday, September 19, 13

Page 40: Complex Architectures in Ember

#1: ROUTES ARE stateS. Kind of.

Thursday, September 19, 13

Page 41: Complex Architectures in Ember

ROUTES ARE STATES 1 var moodManager = Ember.StateManager.create({ 2 initialState: 'good', 3 good: Ember.State.create({ 4 gut: function(){ 5 console.log('Ja'); 6 } 7 }), 8 bad: Ember.State.create({ 9 gut: function(){10 console.log('Nein');11 }12 }),13 quiet: Ember.State.create()14 });15 16 moodManager.send('gut');17 // Ja18 19 moodManager.transitionTo('bad');20 moodManager.send('gut');21 // Nein22 23 moodManager.transitionTo('quiet');24 moodManager.send('gut');25 // Uncaught Error: <Ember.StateManager:ember138> could not respond to event gut in state quiet.

Thursday, September 19, 13

Page 42: Complex Architectures in Ember

ROUTES ARE STATES 1 App = Ember.Application.create(); 2 3 App.Router.map(function(){ 4 this.route('good'); 5 this.route('bad'); 6 this.route('quiet'); 7 }); 8 9 App.GoodRoute = Ember.Route.extend({10 actions: { gut: function(){ console.log('Ja'); } }11 });12 13 App.BadRoute = Ember.Route.extend({14 actions: { gut: function(){ console.log('Nein'); } }15 });16 17 App.QuietRoute = Ember.Route.extend();

1 {{! application.hbs }}2 <a {{action "gut"}}>Gut?</a>3 {{#link-to "good"}}Get happy{{/link-to}}4 {{#link-to "bad"}}Get sour{{/link-to}}5 {{#link-to "quiet"}}Get quiet{{/link-to}}

Thursday, September 19, 13

Page 43: Complex Architectures in Ember

ROUTES ARE STATES 1 App = Ember.Application.create(); 2 3 App.Router.map(function(){ 4 this.route('good'); 5 this.route('bad'); 6 this.route('quiet'); 7 }); 8 9 App.GoodRoute = Ember.Route.extend({10 actions: { gut: function(){ console.log('Ja'); } }11 });12 13 App.BadRoute = Ember.Route.extend({14 actions: { gut: function(){ console.log('Nein'); } }15 });16 17 App.QuietRoute = Ember.Route.extend();

1 {{! application.hbs }}2 <a {{action "gut"}}>Gut?</a>3 {{#link-to "good"}}Get happy{{/link-to}}4 {{#link-to "bad"}}Get sour{{/link-to}}5 {{#link-to "quiet"}}Get quiet{{/link-to}}

ACTIOns always hit the leaf route, regardless

of where they fire from

Thursday, September 19, 13

Page 44: Complex Architectures in Ember

ROUTES ARE STATES 1 App = Ember.Application.create(); 2 3 App.Router.map(function(){ 4 this.route('good'); 5 this.route('bad'); 6 this.route('quiet'); 7 }); 8 9 App.GoodRoute = Ember.Route.extend({10 actions: { gut: function(){ console.log('Ja'); } }11 });12 13 App.GoodController = Ember.Controller.extend({14 actions: { gut: function(){ console.log('ignored :-('); } }15 });

1 {{! application.hbs }}2 <a {{action "gut"}}>Gut?</a>3 {{#link-to "good"}}Get happy{{/link-to}}4 {{#link-to "bad"}}Get sour{{/link-to}}5 {{#link-to "quiet"}}Get quiet{{/link-to}}

BUT the TEMPLATE DECIDES WHICH CONTROLLERS SEE

THAT ACTION

Thursday, September 19, 13

Page 45: Complex Architectures in Ember

ACTIONS ON CONTROLLERS

• couples template to controller• should not force use of NEEDs

ACTIONS ON routes

• have access to all controllers• handled from any template on a page

Thursday, September 19, 13

Page 46: Complex Architectures in Ember

Choose where to put an action.

Thursday, September 19, 13

Page 47: Complex Architectures in Ember

Routes and templates decide how actions propagate the controller/route tree, scope access

to dependencies, and are most subject to external constraints.

Thursday, September 19, 13

Page 48: Complex Architectures in Ember

1 <div style="clear:both"> 2 <div style="float:left"> 3 {{input value=title}} 4 {{textarea value=body}} 5 </div> 6 <div style="float:left"> 7 {{post-preview markdown=body viewName="preview"}} 8 </div> 9 </div>10 11 <div style="clear:both">12 <button style="float:left" {{action "preview" target=view.preview}}>Preview</button>13 <button style="float:left" {{action "submit"}}>Submit</button>14 </div>

setting viewName causes a property named

“preview” to be added on the parentview of that

view with it’s own instance

that “preview” property can be set as a target

Thursday, September 19, 13

Page 49: Complex Architectures in Ember

• Components access nothing• views access parentView, controller• controllers access A target (the route or a parent controller) AND OTHER CONTROLLERS via needs• routes access all controllers and models• CHEAT WITH REGISTER/INJECT

Thursday, September 19, 13

Page 50: Complex Architectures in Ember

CHEAT WITH REGISTER/INJECT

1 App = Ember.Application.create(); 2 3 App.inject('route', 'session', 'controller:session'); 4 5 App.IndexRoute = Ember.Route.extend({ 6 beforeModel: function(){ 7 console.log( this.get('session.user.name') ); 8 } 9 });10 11 App.SessionController = Ember.Controller.extend({12 user: { name: 'Bob' }13 });

Thursday, September 19, 13

Page 51: Complex Architectures in Ember

1 {{render "post"}}2 {{render "post" post}}3 {{component content=post}}4 {{view App.PostView contentBinding="post"}}5 {{template "post"}}

THIS

dictates part of your architecture

Thursday, September 19, 13

Page 52: Complex Architectures in Ember

When confused about an app, look to the templates and the routes first.

Thursday, September 19, 13

Page 53: Complex Architectures in Ember

“Controllers have lots of features!”

AKA

BUT PLEASE DON’t MAKE THEM CLEVER

Thursday, September 19, 13

Page 54: Complex Architectures in Ember

“I AM SO CLEVER THAT SOMETIMES I DON’T

UNDERSTAND A SINGLE WORD OF WHAT I AM SAYING”

Oscar Wilde

Thursday, September 19, 13

Page 55: Complex Architectures in Ember

Clever Controller 1

1 App.BooksController = Ember.ArrayController.extend({2 needs: ['library'],3 content: Em.computed.alias('controllers.library.books')4 });

Thursday, September 19, 13

Page 56: Complex Architectures in Ember

Clever Controller 1

1 App.BooksController = Ember.ArrayController.extend({2 needs: ['library'],3 content: Em.computed.alias('controllers.library.books')4 });

assumption about library controller

Assumed to only be attached to a route (no item controller)

assumes books belong to a single library

Thursday, September 19, 13

Page 57: Complex Architectures in Ember

LESS Clever Controller 1

1 App.BooksRoute = Ember.Route.extend({2 model: function(){3 this.modelFor('library').get('books')4 }5 });6 7 // The Ember controller provided by convention is sufficient.

Thursday, September 19, 13

Page 58: Complex Architectures in Ember

1 <div> 2 <ul class="tabs"> 3 <li {{action "openSignIn"}}>Sign In</li> 4 <li {{action "openSignUp"}}>Sign Up</li> 5 <li {{action "openForgotPassword"}}>Forgot Password</li> 6 </ul> 7 {{#if isSignInOpen}}{{template "sign_in"}}{{/if}} 8 {{#if isSignUpOpen}}{{template "sign_up"}}{{/if}} 9 {{#if isForgotPasswordOpen}}{{template "forgot_password"}}{{/if}}10 </div>

Clever Controller 2 1 App.SessionController = Em.Controller.extend({ 2 isSignInOpen: false, 3 isSignUpOpen: false, 4 isForgotPasswordOpen: false, 5 6 actions: { 7 closeOptions: function(){ 8 this.setProperties({ 9 isSignInOpen: false,10 isSignUpOpen: false,11 isForgotPasswordOpen: false12 });13 },14 openSignIn: function(){ this.closeOptions(); this.set('isSignUpOpen', true); },15 openSignUp: function(){ this.closeOptions(); this.set('isSignUpOpen', true); },16 openForgotPassword: function(){ this.closeOptions(); this.set('isForgotPasswordOpen', true); }17 }18 });

Thursday, September 19, 13

Page 59: Complex Architectures in Ember

1 <div> 2 <ul class="tabs"> 3 <li {{action "openSignIn"}}>Sign In</li> 4 <li {{action "openSignUp"}}>Sign Up</li> 5 <li {{action "openForgotPassword"}}>Forgot Password</li> 6 </ul> 7 {{#if isSignInOpen}}{{template "sign_in"}}{{/if}} 8 {{#if isSignUpOpen}}{{template "sign_up"}}{{/if}} 9 {{#if isForgotPasswordOpen}}{{template "forgot_password"}}{{/if}}10 </div>

Clever Controller 2 1 App.SessionController = Em.Controller.extend({ 2 isSignInOpen: false, 3 isSignUpOpen: false, 4 isForgotPasswordOpen: false, 5 6 actions: { 7 closeOptions: function(){ 8 this.setProperties({ 9 isSignInOpen: false,10 isSignUpOpen: false,11 isForgotPasswordOpen: false12 });13 },14 openSignIn: function(){ this.closeOptions(); this.set('isSignUpOpen', true); },15 openSignUp: function(){ this.closeOptions(); this.set('isSignUpOpen', true); },16 openForgotPassword: function(){ this.closeOptions(); this.set('isForgotPasswordOpen', true); }17 }18 });

NOT. VERY. DRY.

NEW PANELS MUST BE ADDED ON COnTROLLER

NEW PANELS MUST BE ADDED IN TEMPLATEThursday, September 19, 13

Page 60: Complex Architectures in Ember

1 <div> 2 <ul class="tabs"> 3 <li {{action "openSignIn"}}>Sign In</li> 4 <li {{action "openSignUp"}}>Sign Up</li> 5 <li {{action "openForgotPassword"}}>Forgot Password</li> 6 </ul> 7 {{#if isSignInOpen}}{{template "sign_in"}}{{/if}} 8 {{#if isSignUpOpen}}{{template "sign_up"}}{{/if}} 9 {{#if isForgotPasswordOpen}}{{template "forgot_password"}}{{/if}}10 </div>

Clever Controller 2 1 App.SessionController = Em.Controller.extend({ 2 isSignInOpen: false, 3 isSignUpOpen: false, 4 isForgotPasswordOpen: false, 5 6 actions: { 7 closeOptions: function(){ 8 this.setProperties({ 9 isSignInOpen: false,10 isSignUpOpen: false,11 isForgotPasswordOpen: false12 });13 },14 openSignIn: function(){ this.closeOptions(); this.set('isSignUpOpen', true); },15 openSignUp: function(){ this.closeOptions(); this.set('isSignUpOpen', true); },16 openForgotPassword: function(){ this.closeOptions(); this.set('isForgotPasswordOpen', true); }17 }18 });

NAV & DISPLAYED PANEL TIGHTLY COUPLED

panels cannot be controlled from other scopes

Thursday, September 19, 13

Page 61: Complex Architectures in Ember

LESS Clever Controller 2

1 <div>2 <ul class="tabs">3 <li {{bind-attr class="isSignInOpen:active"}}{{action "openPanel" "signIn"}}>Sign In</li>4 <li {{bind-attr class="isSignUpOpen:active"}}{{action "openPanel" "signUp"}}>Sign Up</li>5 <li {{bind-attr class="isForgotPasswordOpen:active"}}{{action "openPanel" "forgotPassword"}}>Forgot Password</li>6 </ul>7 {{outlet "panel"}}8 </div>

1 App.SessionRoute = Ember.Router.extend({ 2 setupController: function(){ 3 this._super.apply(this, arguments); 4 Em.run.once(this, function(){ 5 this.send('openPanel', 'signIn'); 6 }); 7 }, 8 actions: { 9 openPanel: function(panel){10 this.controller.set('openPanel', panel);11 this.render('panels/'+panel, {12 into: 'session',13 outlet: 'panel'14 });15 }16 }17 });18 19 App.SessionController = Ember.Controller.extend({20 isSignInOpen: Em.computed.equal('openPanel', 'signIn'),21 isSignUpOpen: Em.computed.equal('openPanel', 'signUp'),22 isForgotPasswordOpen: Em.computed.equal('openPanel', 'forgotPassword')23 });

Thursday, September 19, 13

Page 62: Complex Architectures in Ember

1 {{#each controller}}2 {{name}}3 {{/each}}

Clever Controller 3

1 App = Ember.Application.create(); 2 3 App.IndexRoute = Ember.Route.extend({ 4 model: function(){ 5 return Ember.A([ 6 { name: 'Munster' }, 7 { name: 'Alpine Lace' }, 8 { name: 'Tome' } 9 ]);10 }11 });

Thursday, September 19, 13

Page 63: Complex Architectures in Ember

1 {{#each controller}}2 {{name}}3 {{/each}}

Clever Controller 3

1 App = Ember.Application.create(); 2 3 App.IndexRoute = Ember.Route.extend({ 4 model: function(){ 5 return Ember.A([ 6 { name: 'Munster' }, 7 { name: 'Alpine Lace' }, 8 { name: 'Tome' } 9 ]);10 }11 });

APPle invasion! apple invasion! apple invasion!

Thursday, September 19, 13

Page 64: Complex Architectures in Ember

1 {{#each controller}}2 {{name}}3 {{/each}}

Clever Controller 3 1 App = Ember.Application.create(); 2 3 App.IndexRoute = Ember.Route.extend({ 4 model: function(){ 5 return Ember.A([ 6 { name: 'Munster' }, 7 { name: 'Alpine Lace' }, 8 { name: 'Tome' } 9 ]);10 }11 });12 13 App.IndexController = Ember.ArrayController.extend({14 itemController: 'fruitInvasion'15 });16 17 App.FruitInvasionController = Ember.ObjectController.extend({18 name: function(){19 return 'Apple invasion!';20 }.property()21 });

APPle invasion! apple invasion! apple invasion!

Changes context in the template from outside the

template.

Thursday, September 19, 13

Page 65: Complex Architectures in Ember

1 {{#each controller itemController='fruitInvasion'}}2 {{name}}3 {{/each}}

LESS Clever Controller 3

1 App = Ember.Application.create(); 2 3 App.IndexRoute = Ember.Route.extend({ 4 model: function(){ 5 return Ember.A([ 6 { name: 'Munster' }, 7 { name: 'Alpine Lace' }, 8 { name: 'Tome' } 9 ]);10 }11 });12 13 App.FruitInvasionController = Ember.ObjectController.extend({14 name: function(){15 return 'Apple invasion!';16 }.property()17 });

Thursday, September 19, 13

Page 66: Complex Architectures in Ember

“TOO CLEVER IS DUMB”Ogden Nash

Thursday, September 19, 13

Page 67: Complex Architectures in Ember

TL;DR

Thursday, September 19, 13

Page 68: Complex Architectures in Ember

Don’t work so hard in controllers.

Thursday, September 19, 13

Page 69: Complex Architectures in Ember

Look to routes and templates for your application architecture.

Thursday, September 19, 13

Page 70: Complex Architectures in Ember

Thanks!

@mixonic

httP://madhatted.com

[email protected]

Thursday, September 19, 13