Hi. I’m Matthew.
@mixonic
httP://madhatted.com
matt.beale@madhatted.com
201 Created
We build õ-age apps with Ember.js. We take
teams from £ to • in no time flat.
http://bit.ly/ember-nyc-edge
WHAT IS EMBER MADE OF?
• MVC FRamework
• MVC FRamework
• Models
• Views
• Controllers
• MVC FRamework
• Models
• Views
• Controllers
• Components
• ROUTES
• Router
• templates
• MVC FRamework
• Models
• Views
• Controllers
• Components • Store
• Serializers
• ROUTES
• adapters
• Router
• templates
SO MANY THINGS
What makes a

router different from a template?
What makes a

router different from a template?
• maps routes to states
• only one router
• instance of a class
• App.Router
• Has access to store (ED)
What makes a

router different from a template?
• maps routes to states
• only one router
• instance of a class
• App.Router
• Has access to store (ED)

• presents html
• many templates
• just functions
• on Ember.templates
What makes things similar?
What makes things similar?
• Shared Interface
1 App.IndexView = Ember.Object.extend({
2
click: function(){ alert('click!'); },
3
appendTo: function(){ /* ... */ },
4
render: function(){ /* ... */ },
5
rerender: function(){ /* ... */ }
6
// ...
7 });
1 App.IndexView = Ember.Object.extend({
2
click: function(){ alert('click!'); },
3
appendTo: function(){ /* ... */ },
4
render: function(){ /* ... */ },
5
rerender: function(){ /* ... */ }
6
// ...
7 });
1 App.WidgetView = Ember.Object.extend({
2
click: function(){ alert('widget!'); },
3
appendTo: function(){ /* ... */ },
4
render: function(){ /* ... */ },
5
rerender: function(){ /* ... */ }
6
// ...
7 });
1 Ember.View = Ember.Object.extend({
2
appendTo: function(){ /* ... */ },
3
render: function(){ /* ... */ },
4
rerender: function(){ /* ... */ }
5 });
1 App.IndexView = Ember.View.extend({
2
click: function(){ alert('click!'); }
3 });
1 App.WidgetView = Ember.View.extend({
2
click: function(){ alert('widget!'); }
3 });
What makes things similar?
• Shared Interface (superclass?)
App.IndexView = Ember.View.extend({ // ...
What makes things similar?
• Shared Interface (superclass?)

App.IndexView = Ember.View.extend({ // ...

• SHARED DEPENDENCIES
1 App.IndexRoute = Ember.Route.extend({
2
actions: {
3
didTransition: function(){
4
Ember.run.once(this, function(){
5
trackAnalytics(this.get('router.url'));
6
});
7
}
8
}
9 });
What makes things similar?
• Shared Interface (superclass?)

App.IndexView = Ember.View.extend({ // ...

• SHARED DEPENDENCIES
this.get('router'); // In a route
What makes things similar?
• Shared Interface (superclass?)

App.IndexView = Ember.View.extend({ // ...

• SHARED DEPENDENCIES

this.get('router'); // In a route

• SHAREd USAGE

Ember.Component.extend({

!
!
Ember.Controller.extend({

!
!
Ember.Handlebars.compile("

!
Is A

Ember.Component.extend({

!
!
Ember.Controller.extend({

!
!
Ember.Handlebars.compile("

!

!
!
Class

!
!
Class

!
!
Function
Is A

Ember.Component.extend({

!
!
Ember.Controller.extend({

!
!
Ember.Handlebars.compile("

!

Instantiate?

!
!

!
!
!
!
!
!
!
!
!

Class

!
!
Class

!
!
Function

✓
✓
✗
Is A

Ember.Component.extend({

!
!
Ember.Controller.extend({

!
!
Ember.Handlebars.compile("

!

Instantiate?

New every time?

!
!

!
!
!
!
!
!
!
!
!

!
!
!
!
!
!
!
!
!

Class

!
!
Class

!
!
Function

✓
✓
✗

✓
✗
✗
What makes things similar?
• Shared Interface (superclass?)

App.IndexView = Ember.View.extend({ // ...

• SHARED DEPENDENCIES

this.get('router'); // In a route

• SHAREd USAGE

Instantiate all the controllers, but return the same one each time
What makes things similar?
• Shared Interface (superclass?)

App.IndexView = Ember.View.extend({ // ...

• SHARED DEPENDENCIES

this.get('router'); // In a route

• SHAREd USAGE

Instantiate all the controllers, but return the same one each time

• SHARED DISCOVERY
What makes things similar?
• Shared Interface (superclass?)

App.IndexView = Ember.View.extend({ // ...

• SHARED DEPENDENCIES

this.get('router'); // In a route

• SHAREd USAGE

Instantiate all the controllers, but return the same one each time

• SHARED DISCOVERY
App.ColorPickerComponent

Ember.TEMPLATES['index']

App.Car
What makes things similar?
• Shared Interface (superclass?)

App.IndexView = Ember.View.extend({ // ...

• SHARED DEPENDENCIES

this.get('router'); // In a route

• SHAREd USAGE

Instantiate all the controllers, but return the same one each time

• SHARED DISCOVERY
App.ColorPickerComponent

Ember.TEMPLATES['index']

App.Car
What makes things similar?
• Shared Interface (superclass?)

App.IndexView = Ember.View.extend({ // ...

• SHARED DEPENDENCIES

this.get('router'); // In a route

• SHAREd USAGE

Instantiate all the controllers, but return the same one each time

• SHARED DISCOVERY
App.ColorPickerComponent

Ember.TEMPLATES['index']

App.Car
CONTAINERS
The container organizes
Ember’s building blocks.
The container re-organizes
Ember’s building blocks.
The container organizes new
building blocks.
register/lookup
register a factory into a container
1
2
3
4
5
6
7
8
9
10
11
12

var WorkerPool = Ember.Object.extend({
workers: /* an array of workers */,
limit: 3,
submitJob: /* take a job and run it, add workers if needed */
});
var container = new Ember.Container();
container.register('workerPool:main', WorkerPool);
container.lookup('workerPool:main'); // -> Instance of WorkerPool
container.lookup('workerPool:main'); // -> Same instance of WorkerPool
register a factory into a container
1
2
3
4
5
6
7
8
9
10
11
12

var WorkerPool = Ember.Object.extend({
workers: /* an array of workers */,
limit: 3,
submitJob: /* take a job and run it, add workers if needed */
});
var container = new Ember.Container();
container.register('workerPool:main', WorkerPool);
container.lookup('workerPool:main'); // -> Instance of WorkerPool
container.lookup('workerPool:main'); // -> Same instance of WorkerPool

singleton (always the same instance)
register like factories into a container
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

var WorkerPool = Ember.Object.extend({
workers: /* an array of workers */,
limit: 3,
submitJob: /* take a job and run it, add workers if needed */
});
var BigPool = WorkerPool.extend({ limit: 10 });
var container = new Ember.Container();
container.register('workerPool:main', WorkerPool);
container.register('workerPool:big', BigPool);
container.lookup('workerPool:main'); // -> Instance of WorkerPool
container.lookup('workerPool:big'); // -> Instance of BigPool
register like factories into a container
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

var WorkerPool = Ember.Object.extend({
workers: /* an array of workers */,
limit: 3,
submitJob: /* take a job and run it, add workers if needed */
});
var BigPool = WorkerPool.extend({ limit: 10 });
var container = new Ember.Container();
container.register('workerPool:main', BigPool);
container.register('workerPool:big', BigPool);
container.lookup('workerPool:main'); // -> Instance of BigPool
container.lookup('workerPool:big'); // -> Different instance of BigPool
register non-singleton factories into a container

1
2
3
4
5
6
7
8
9
10
11

var Worker = Ember.Object.extend({
run: function(){ /* run a job */ }
});
var container = new Ember.Container();
container.register('worker:sync', Worker, {singleton: false});
container.lookup('worker:sync'); // -> Instance of Worker
container.lookup('worker:sync'); // -> New instance of Worker
container.lookup('worker:sync'); // -> New instance of Worker!
register non-class factories into a container

1
2
3
4
5
6
7
8
9
10
11

var container = new Ember.Container();
container.register('logger:alert', window.alert, {instantiate: false});
container.register('logger:console', function(msg){
window.console.log(msg);
}, {instantiate: false});
container.lookup('logger:alert'); // -> alert function
container.lookup('logger:console'); // -> console.log function
container.lookup('logger:console')('Howdy!'); // -> "Howdy!"
injection + dependency lookup
a wild dependency appears
1
2
3
4
5
6
7
8
9
10
11
12
13
14

var WorkerPool = Ember.Object.extend({
workers: /* an array of workers */,
limit: 3,
submitJob: function(job){
this.get('logger')('Began job.')
/* take a job and run it, add workers if needed */
}
});
var container = new Ember.Container();
container.register('workerPool:main', WorkerPool);
var pool = container.lookup('workerPool:main');
pool.submitJob(job);
a wild dependency appears
1
2
3
4
5
6
7
8
9
10
11
12
13
14

var WorkerPool = Ember.Object.extend({
workers: /* an array of workers */,
limit: 3,
submitJob: function(job){
this.get('logger')('Began job.')
/* take a job and run it, add workers if needed */
}
});

Need a logger

var container = new Ember.Container();
container.register('workerPool:main', WorkerPool);
var pool = container.lookup('workerPool:main');
pool.submitJob(job);
Inject a dependency on a factory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

var WorkerPool = Ember.Object.extend({
workers: /* an array of workers */,
limit: 3,
submitJob: function(job){
this.get('logger')('Began job.')
/* take a job and run it, add workers if needed */
}
});
var container = new Ember.Container();
container.register('workerPool:main', WorkerPool);
container.register('logger:alert', window.alert, {instantiate: false});
container.injection('workerPool:main', 'logger', 'logger:alert');
var pool = container.lookup('workerPool:main');
pool.submitJob(job);
Inject a dependency on a factory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

var WorkerPool = Ember.Object.extend({
workers: /* an array of workers */,
limit: 3,
submitJob: function(job){
this.get('logger')('Began job.')
/* take a job and run it, add workers if needed */
}
});
var container = new Ember.Container();
container.register('workerPool:main', WorkerPool);
container.register('logger:alert', window.alert, {instantiate: false});
container.injection('workerPool:main', 'logger', 'logger:alert');
var pool = container.lookup('workerPool:main');
pool.submitJob(job);
Inject a dependency on a factory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

var WorkerPool = Ember.Object.extend({
workers: /* an array of workers */,
limit: 3,
submitJob: function(job){
this.get('logger')('Began job.')
/* take a job and run it, add workers if needed */
}
});
var container = new Ember.Container();
container.register('workerPool:main', WorkerPool);
container.register('logger:alert', window.alert, {instantiate: false});
container.injection('workerPool:main', 'logger', 'logger:alert');
var pool = container.lookup('workerPool:main');
pool.submitJob(job);

control over the dependency
Inject a dependency on a type
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

var WorkerPool = Ember.Object.extend({
workers: /* an array of workers */,
limit: 3,
submitJob: function(job){
this.get('logger')('Began job.')
/* take a job and run it, add workers if needed */
}
});
var BigPool = WorkerPool.extend({ limit: 10 });
var container = new Ember.Container();
container.register('workerPool:main', WorkerPool);
container.register('workerPool:big', BigPool);
container.register('logger:alert', window.alert, {instantiate: false});
container.injection('workerPool', 'logger', 'logger:alert');
var pool = container.lookup('workerPool:big');
pool.submitJob(job);
Inject a dependency on a type
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

var WorkerPool = Ember.Object.extend({
workers: /* an array of workers */,
limit: 3,
submitJob: function(job){
this.get('logger')('Began job.')
/* take a job and run it, add workers if needed */
}
});
var BigPool = WorkerPool.extend({ limit: 10 });
var container = new Ember.Container();
container.register('workerPool:main', WorkerPool);
container.register('workerPool:big', BigPool);
container.register('logger:alert', window.alert, {instantiate: false});
container.injection('workerPool', 'logger', 'logger:alert');
var pool = container.lookup('workerPool:big');
pool.submitJob(job);
Inject a dependency on a type
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

var WorkerPool = Ember.Object.extend({
workers: /* an array of workers */,
limit: 3,
submitJob: function(job){
this.get('logger')('Began job.')
/* take a job and run it, add workers if needed */
}
});
var BigPool = WorkerPool.extend({ limit: 10 });
var container = new Ember.Container();
container.register('workerPool:main', WorkerPool);
container.register('workerPool:big', BigPool);
container.register('logger:alert', window.alert, {instantiate: false});
container.injection('workerPool', 'logger', 'logger:alert');
var pool = container.lookup('workerPool:big');
pool.submitJob(job);

control over the dependency
a wild dependency appears
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

var WorkerPool = Ember.Object.extend({
workers: /* an array of workers */,
limit: 3,
submitJob: function(job){
this.get('logger')('Began job.')
/* take a job and run it, add workers if needed */
}
});

Need worker

var BigPool = WorkerPool.extend({ limit: 10 });
var container = new Ember.Container();
container.register('workerPool:main', WorkerPool);
container.register('workerPool:big', BigPool);
container.register('logger:alert', window.alert, {instantiate: false});
container.injection('workerPool', 'logger', 'logger:alert');
var pool = container.lookup('workerPool:big');
pool.submitJob(job);
Lookup a dependency
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

var Worker = Ember.Object.extend({
run: function(){ /* run a job */ }
});
var WorkerPool = Ember.Object.extend({
workers: /* an array of workers */,
noWorkerAvailable: /* check for a free worker */,
limit: 3,
submitJob: function(job){
if (this.get('noWorkerAvailable')) {
var worker = this.container.lookup('worker:sync');
worker.run(job);
this.get('workers').pushObject(worker);
} // else find a free worker in the pool and run
}
});
var container = new Ember.Container();
container.register('workerPool:main', WorkerPool);
container.register('worker:sync', Worker, {singleton: false});
var pool = container.lookup('workerPool:main');
pool.submitJob(job);
Lookup a dependency
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

var Worker = Ember.Object.extend({
run: function(){ /* run a job */ }
});
var WorkerPool = Ember.Object.extend({
workers: /* an array of workers */,
noWorkerAvailable: /* check for a free worker */,
limit: 3,
submitJob: function(job){
if (this.get('noWorkerAvailable')) {
var worker = this.container.lookup('worker:sync');
worker.run(job);
this.get('workers').pushObject(worker);
} // else find a free worker in the pool and run
}
});
var container = new Ember.Container();
container.register('workerPool:main', WorkerPool);
container.register('worker:sync', Worker, {singleton: false});
var pool = container.lookup('workerPool:main');
pool.submitJob(job);
Lookup a dependency
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

var Worker = Ember.Object.extend({
run: function(){ /* run a job */ }
});
var WorkerPool = Ember.Object.extend({
workers: /* an array of workers */,
noWorkerAvailable: /* check for a free worker */,
limit: 3,
submitJob: function(job){
if (this.get('noWorkerAvailable')) {
var worker = this.container.lookup('worker:sync');
worker.run(job);
this.get('workers').pushObject(worker);
} // else find a free worker in the pool and run
}
});

control over the dependency

var container = new Ember.Container();
container.register('workerPool:main', WorkerPool);
container.register('worker:sync', Worker, {singleton: false});
var pool = container.lookup('workerPool:main');
pool.submitJob(job);
dynamically Lookup a dependency
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

var WorkerPool = Ember.Object.extend({
workers: /* an array of workers */,
noWorkerAvailable: /* check for a free worker */,
limit: 3,
submitJob: function(job){
if (this.get('noWorkerAvailable')) {
var worker = this.container.lookup(
'worker:'+(job.get('isAsync') ? 'async' : 'sync')
);
worker.run(job);
this.get('workers').pushObject(worker);
} // else find a free worker in the pool and run
}
});
var container = new Ember.Container();
container.register('workerPool:main', WorkerPool);
container.register('worker:sync', Worker, {singleton: false});
container.register('worker:async', AsyncWorker, {singleton: false});
var pool = container.lookup('workerPool:main');
pool.submitJob(job);
Injection places control
outside the target, lookup
makes it internal.
resolving factories
resolve to namespace
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

function capitalize(str){
return str.charAt(0).toUpperCase() + str.slice(1);
}
var MyScope = {};
MyScope.SyncWorker = Ember.Object.extend({
run: function(){ /* run a job */ }
});
var container = new Ember.Container();
container.resolve = function(name){
var parts = name.split(':'),
className = capitalize(parts[1])+capitalize(parts[0]);
return MyScope[className];
}
var worker = container.lookup('worker:sync', {instantiate: false});
resolve to AMD module
1
2
3
4
5
6
7
8
9
10
11
12
13
14

define('workers/sync', function(){
return Ember.Object.extend({
run: function(){ /* run a job */ }
});
});
var container = new Ember.Container();
container.resolve = function(name){
var parts = name.split(':'),
moduleName = parts[0]+'s/'+parts[1];
return require(moduleName);
}
var worker = container.lookup('worker:sync', {instantiate: false});
THE application CONTAINER
Ember applications use a
single container.
Several ways to access it
• Everything from a container has this.container

1 App.IndexView = Ember.View.extend({
2
router: function(){
3
return this.container.lookup('router:main');
4
}.property()
5 });
Several ways to access it
• Everything from a container has this.container


router: function(){ return this.container.lookup('router:main'); }.property()!

• App.register, App.Inject


App.register('analytics:google', App.GoogleAnalytics);!
App.inject('controller', 'analytics', 'analytics:google');!
Several ways to access it
• Everything from a container has this.container


router: function(){ return this.container.lookup('router:main'); }.property()!

• App.register, App.Inject


App.register('analytics:google', App.GoogleAnalytics);!
App.inject('controller', 'analytics', 'analytics:google');!

• In Initializers

App.initializer({
name: 'analytics',
/* before: 'optionallyBeforeThis' */
initialize: function(container, application){
application.register('analytics:google', App.GoogleAnalytics);
container.injection('controller', 'analytics', 'analytics:google');
}
})
Several ways to access it
• Everything from a container has this.container


router: function(){ return this.container.lookup('router:main'); }.property()!

• App.register, App.Inject


App.register('analytics:google', App.GoogleAnalytics);!
App.inject('controller', 'analytics', 'analytics:google');!

• In Initializers


App.initializer({ initialize: function(container, application){ // ...

• needs

App.AnalyticsController = Ember.Controller.extend();
App.IndexController = Ember.Controller.extend({
needs: ['analytics'],
analytics: Ember.computed.alias('controllers.analytics')
});

!
Several ways to access it
• Everything from a container has this.container


router: function(){ return this.container.lookup('router:main'); }.property()!

• App.register, App.Inject


App.register('analytics:google', App.GoogleAnalytics);!
App.inject('controller', 'analytics', 'analytics:google');!

• In Initializers


App.initializer({ initialize: function(container, application){ // ...

• needs

App.AnalyticsController = Ember.Controller.extend();
App.IndexController = Ember.Controller.extend({
needs: ['analytics'],
analytics: Ember.computed.alias('controllers.analytics')
});

!
…and one unsafe way.
// Never, ever use this in application code
App.__container__
how the container
powers ember
ember Finds a template

1 Ember.View = Ember.CoreView.extend({
2
3
templateForName: function(name, type) {
4
if (!name) { return; }
5
Ember.assert("templateNames are not allowed to contain periods: "+name, name.indexOf('.') ==
6
7
// the defaultContainer is deprecated
8
var container = this.container || (Ember.Container && Ember.Container.defaultContainer);
9
return container && container.lookup('template:' + name);
10
},
11
// ...
12 });

view.js
ember Finds a template
1 Ember.DefaultResolver = Ember.Object.extend({
2
3
resolveTemplate: function(parsedName) {
4
var templateName = parsedName.fullNameWithoutType.replace(/./g, '/');
5
6
if (Ember.TEMPLATES[templateName]) {
7
return Ember.TEMPLATES[templateName];
8
}
9
10
templateName = decamelize(templateName);
11
if (Ember.TEMPLATES[templateName]) {
12
return Ember.TEMPLATES[templateName];
13
}
14
},
15
// ...
16 });

resolver.js
ember decides to generate a controller

1 Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
2
3
setup: function(context, transition) {
4
var controllerName = this.controllerName || this.routeName,
5
controller = this.controllerFor(controllerName, true);
6
if (!controller) {
7
controller = this.generateController(controllerName, context);
8
}
9
10
// ...

route.js
ember decides to generate a controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

Ember.generateControllerFactory = function(container, controllerName, context) {!
var Factory, fullName, instance, name, factoryName, controllerType;!
!
if (context && Ember.isArray(context)) {!
controllerType = 'array';!
} else if (context) {!
controllerType = 'object';!
} else {!
controllerType = 'basic';!
}!
!
factoryName = 'controller:' + controllerType;!
!
Factory = container.lookupFactory(factoryName).extend({!
isGenerated: true,!
toString: function() {!
return "(generated " + controllerName + " controller)";!
}!
});!
!
fullName = 'controller:' + controllerName;!
!
container.register(fullName, Factory);!
!
return Factory;!
};!
controller_for.js
ember data injects a store
1 Ember.onLoad('Ember.Application', function(Application) {
2
3
// ... adapters, serializers, transforms, etc
4
5
Application.initializer({
6
name: "injectStore",
7
before: "store",
8
9
initialize: function(container, application) {
10
application.inject('controller', 'store', 'store:main');
11
application.inject('route', 'store', 'store:main');
12
application.inject('serializer', 'store', 'store:main');
13
application.inject('dataAdapter', 'store', 'store:main');
14
}
15
});
16
17 });
initializers.js
How to re-organzie types
Make an li tag active for sub routes (bootstrap nav)
1 App.ActiveLiComponent = Ember.Component.extend({
2
tagName: 'li',
3
classNameBindings: ['isActive:active:inactive'],
4
5
router: function(){
6
return this.container.lookup('router:main');
7
}.property(),
8
9
isActive: function(){
10
var currentWhen = this.get('currentWhen');
11
return this.get('router').isActive(currentWhen);
12
}.property('router.url', 'currentWhen')
13 });

http://emberjs.jsbin.com/iFEvATE/2/edit?html,js,output
Isolate tests with a container
1 var container, component, router;!
2 module("ActiveLiComponent tests", {!
3
setup: function(){!
4
container = new Ember.Container();!
5
container.register('component:active-li', App.ActiveLiComponent);!
6
container.register('router:main', Ember.Object.create({!
7
url: '/',!
8
isActive: function(){}!
9
}), {instantiate: false});!
10
component = container.lookup('component:active-li');!
11
router = container.lookup('router:main');!
12
},!
13
teardown: function() {!
14
Ember.run(container, 'destroy');!
15
}!
16 });!

http://emberjs.jsbin.com/aGILoru/1/edit?js,output
Create services with needs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

App.AudioPlayerController = Ember.Controller.extend({
play: function(track){
this.set('currentTrack', play);
1 {{! application.hbs }}
this.set('isPlaying', true);
2 {{render “audio_player"}}
},
3 {{outlet}}
// ...
});
App.FieldElementController = Ember.Controller.extend({
needs: ['audio_player'],
player: Ember.computed.alias('controllers.audio_player'),
actions: {
start: function(){
var player = this.get('player'),
track = this.get('model.track');
player.play(track);
}
}
});
http://to.be/fields/cG8QrM
Isolate tests with a container
1 var container, controller, player;
2 module("FieldElementController tests", {
3
setup: function(){
4
container = new Ember.Container();
5
container.register('controller:field_element', App.FieldElement);
6
container.register('controller:audio_player', Ember.Object.extend({
7
play: function(){}
8
}));
9
controller = container.lookup('controller:field_element');
10
player = container.lookup('controller:audio_player');
11
},
12
teardown: function() {
13
Ember.run(container, 'destroy');
14
}
15 });
make new types
deferred queue uploader
1 ToBe.initializer({
2
name: 'fieldElementUpdater',
3
4
initialize: function(container, application){
5
6
// Register the fieldElementUpdater to the controllers, models, views, routes.
7
container.optionsForType('fieldElementUpdater', {singleton: true});
8
container.register('fieldElementUpdater:main', ToBe.FieldElementUpdater);
9
container.injection('controller', 'fieldElementUpdater', 'fieldElementUpdater:main');
10
container.injection('model',
'fieldElementUpdater', 'fieldElementUpdater:main');
11
container.injection('route',
'fieldElementUpdater', 'fieldElementUpdater:main');
12
13
// Legacy, non-Ember code
14
application.set('fieldElementUpdater', container.lookup('fieldElementUpdater:main'));
15
16
}
17 });
http://to.be/fields/iJYzt-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

Ember.Application.initializer({
name: 'adapter',
before: 'authentication',
initialize: function(container, app){
import FirebaseAdapter from 'appkit/adapters/firebase';
container.register('adapter:firebase', FirebaseAdapter);
app.inject('controller', 'firebase', 'adapter:firebase');
app.inject('route',
'firebase', 'adapter:firebase');
}
});
Ember.Application.initializer({
name: 'authentication',
initialize: function(container, app){
import Session from 'appkit/controllers/session';
container.register('session:main', Session);
app.inject('controller', 'session', 'session:main');
app.inject('route',
'session', 'session:main');
import FirebaseAuth from 'appkit/models/firebase_auth';
container.register('session:adapter', FirebaseAuth);
app.inject('session:adapter', 'firebase', 'adapter:firebase');
app.inject('session:main',
'adapter', 'session:adapter');
}
});

https://github.com/mixonic/grimvisage

auth
1 var SessionController = Ember.Object.extend({
2
isAuthenticated: false,
3
currentUser: null,
4
afterRedirect: null,
5
6
open: function(credentials){
7
var session = this;
8
return this.get('adapter').open(credentials, this)
9
.then(function(user){
10
session.set('isAuthenticated', true);
11
session.set('currentUser', user);
12
return user;
13
}, Ember.RSVP.reject);
14
},
15
16
fetch: function(){
17
var session = this;
18
return this.get('adapter').fetch(this)
19
.then(function(user){
20
session.set('isAuthenticated', true);
21
session.set('currentUser', user);
22
return user;
23
}, Ember.RSVP.reject);
24
},
25
26
close: function(){
27
var session = this;
28
return this.get('adapter').close(this)
29
.then(function(ref){
30
session.set('isAuthenticated', false);
31
session.set('currentUser', null);
32
return ref;
33
}, Ember.RSVP.reject);
34
}
35 });
36
37 export default SessionController;

session.js

auth
Containers allow the
definition of patterns
beyond MVC.
The resolver and container
power Ember’s

module-filled-future.
Thanks!
@mixonic
httP://madhatted.com
matt.beale@madhatted.com

Containers & Dependency in Ember.js

  • 1.
  • 2.
  • 3.
    201 Created We buildõ-age apps with Ember.js. We take teams from £ to • in no time flat.
  • 4.
  • 5.
    WHAT IS EMBERMADE OF?
  • 6.
  • 7.
    • MVC FRamework •Models • Views • Controllers
  • 8.
    • MVC FRamework •Models • Views • Controllers • Components • ROUTES • Router • templates
  • 9.
    • MVC FRamework •Models • Views • Controllers • Components • Store • Serializers • ROUTES • adapters • Router • templates
  • 10.
  • 11.
    What makes a
 routerdifferent from a template?
  • 12.
    What makes a
 routerdifferent from a template? • maps routes to states • only one router • instance of a class • App.Router • Has access to store (ED)
  • 13.
    What makes a
 routerdifferent from a template? • maps routes to states • only one router • instance of a class • App.Router • Has access to store (ED) • presents html • many templates • just functions • on Ember.templates
  • 14.
  • 15.
    What makes thingssimilar? • Shared Interface
  • 16.
    1 App.IndexView =Ember.Object.extend({ 2 click: function(){ alert('click!'); }, 3 appendTo: function(){ /* ... */ }, 4 render: function(){ /* ... */ }, 5 rerender: function(){ /* ... */ } 6 // ... 7 });
  • 17.
    1 App.IndexView =Ember.Object.extend({ 2 click: function(){ alert('click!'); }, 3 appendTo: function(){ /* ... */ }, 4 render: function(){ /* ... */ }, 5 rerender: function(){ /* ... */ } 6 // ... 7 }); 1 App.WidgetView = Ember.Object.extend({ 2 click: function(){ alert('widget!'); }, 3 appendTo: function(){ /* ... */ }, 4 render: function(){ /* ... */ }, 5 rerender: function(){ /* ... */ } 6 // ... 7 });
  • 18.
    1 Ember.View =Ember.Object.extend({ 2 appendTo: function(){ /* ... */ }, 3 render: function(){ /* ... */ }, 4 rerender: function(){ /* ... */ } 5 }); 1 App.IndexView = Ember.View.extend({ 2 click: function(){ alert('click!'); } 3 }); 1 App.WidgetView = Ember.View.extend({ 2 click: function(){ alert('widget!'); } 3 });
  • 19.
    What makes thingssimilar? • Shared Interface (superclass?) App.IndexView = Ember.View.extend({ // ...
  • 20.
    What makes thingssimilar? • Shared Interface (superclass?)
 App.IndexView = Ember.View.extend({ // ... • SHARED DEPENDENCIES
  • 21.
    1 App.IndexRoute =Ember.Route.extend({ 2 actions: { 3 didTransition: function(){ 4 Ember.run.once(this, function(){ 5 trackAnalytics(this.get('router.url')); 6 }); 7 } 8 } 9 });
  • 22.
    What makes thingssimilar? • Shared Interface (superclass?)
 App.IndexView = Ember.View.extend({ // ... • SHARED DEPENDENCIES this.get('router'); // In a route
  • 23.
    What makes thingssimilar? • Shared Interface (superclass?)
 App.IndexView = Ember.View.extend({ // ... • SHARED DEPENDENCIES
 this.get('router'); // In a route • SHAREd USAGE

  • 24.
  • 25.
  • 26.
  • 27.
    Is A Ember.Component.extend({ ! ! Ember.Controller.extend({ ! ! Ember.Handlebars.compile(" ! Instantiate? New everytime? ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! Class ! ! Class ! ! Function ✓ ✓ ✗ ✓ ✗ ✗
  • 28.
    What makes thingssimilar? • Shared Interface (superclass?)
 App.IndexView = Ember.View.extend({ // ... • SHARED DEPENDENCIES
 this.get('router'); // In a route • SHAREd USAGE
 Instantiate all the controllers, but return the same one each time
  • 29.
    What makes thingssimilar? • Shared Interface (superclass?)
 App.IndexView = Ember.View.extend({ // ... • SHARED DEPENDENCIES
 this.get('router'); // In a route • SHAREd USAGE
 Instantiate all the controllers, but return the same one each time • SHARED DISCOVERY
  • 30.
    What makes thingssimilar? • Shared Interface (superclass?)
 App.IndexView = Ember.View.extend({ // ... • SHARED DEPENDENCIES
 this.get('router'); // In a route • SHAREd USAGE
 Instantiate all the controllers, but return the same one each time • SHARED DISCOVERY App.ColorPickerComponent Ember.TEMPLATES['index'] App.Car
  • 31.
    What makes thingssimilar? • Shared Interface (superclass?)
 App.IndexView = Ember.View.extend({ // ... • SHARED DEPENDENCIES
 this.get('router'); // In a route • SHAREd USAGE
 Instantiate all the controllers, but return the same one each time • SHARED DISCOVERY App.ColorPickerComponent Ember.TEMPLATES['index'] App.Car
  • 32.
    What makes thingssimilar? • Shared Interface (superclass?)
 App.IndexView = Ember.View.extend({ // ... • SHARED DEPENDENCIES
 this.get('router'); // In a route • SHAREd USAGE
 Instantiate all the controllers, but return the same one each time • SHARED DISCOVERY App.ColorPickerComponent Ember.TEMPLATES['index'] App.Car
  • 33.
  • 34.
  • 35.
  • 36.
    The container organizesnew building blocks.
  • 37.
  • 38.
    register a factoryinto a container 1 2 3 4 5 6 7 8 9 10 11 12 var WorkerPool = Ember.Object.extend({ workers: /* an array of workers */, limit: 3, submitJob: /* take a job and run it, add workers if needed */ }); var container = new Ember.Container(); container.register('workerPool:main', WorkerPool); container.lookup('workerPool:main'); // -> Instance of WorkerPool container.lookup('workerPool:main'); // -> Same instance of WorkerPool
  • 39.
    register a factoryinto a container 1 2 3 4 5 6 7 8 9 10 11 12 var WorkerPool = Ember.Object.extend({ workers: /* an array of workers */, limit: 3, submitJob: /* take a job and run it, add workers if needed */ }); var container = new Ember.Container(); container.register('workerPool:main', WorkerPool); container.lookup('workerPool:main'); // -> Instance of WorkerPool container.lookup('workerPool:main'); // -> Same instance of WorkerPool singleton (always the same instance)
  • 40.
    register like factoriesinto a container 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var WorkerPool = Ember.Object.extend({ workers: /* an array of workers */, limit: 3, submitJob: /* take a job and run it, add workers if needed */ }); var BigPool = WorkerPool.extend({ limit: 10 }); var container = new Ember.Container(); container.register('workerPool:main', WorkerPool); container.register('workerPool:big', BigPool); container.lookup('workerPool:main'); // -> Instance of WorkerPool container.lookup('workerPool:big'); // -> Instance of BigPool
  • 41.
    register like factoriesinto a container 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var WorkerPool = Ember.Object.extend({ workers: /* an array of workers */, limit: 3, submitJob: /* take a job and run it, add workers if needed */ }); var BigPool = WorkerPool.extend({ limit: 10 }); var container = new Ember.Container(); container.register('workerPool:main', BigPool); container.register('workerPool:big', BigPool); container.lookup('workerPool:main'); // -> Instance of BigPool container.lookup('workerPool:big'); // -> Different instance of BigPool
  • 42.
    register non-singleton factoriesinto a container 1 2 3 4 5 6 7 8 9 10 11 var Worker = Ember.Object.extend({ run: function(){ /* run a job */ } }); var container = new Ember.Container(); container.register('worker:sync', Worker, {singleton: false}); container.lookup('worker:sync'); // -> Instance of Worker container.lookup('worker:sync'); // -> New instance of Worker container.lookup('worker:sync'); // -> New instance of Worker!
  • 43.
    register non-class factoriesinto a container 1 2 3 4 5 6 7 8 9 10 11 var container = new Ember.Container(); container.register('logger:alert', window.alert, {instantiate: false}); container.register('logger:console', function(msg){ window.console.log(msg); }, {instantiate: false}); container.lookup('logger:alert'); // -> alert function container.lookup('logger:console'); // -> console.log function container.lookup('logger:console')('Howdy!'); // -> "Howdy!"
  • 44.
  • 45.
    a wild dependencyappears 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var WorkerPool = Ember.Object.extend({ workers: /* an array of workers */, limit: 3, submitJob: function(job){ this.get('logger')('Began job.') /* take a job and run it, add workers if needed */ } }); var container = new Ember.Container(); container.register('workerPool:main', WorkerPool); var pool = container.lookup('workerPool:main'); pool.submitJob(job);
  • 46.
    a wild dependencyappears 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var WorkerPool = Ember.Object.extend({ workers: /* an array of workers */, limit: 3, submitJob: function(job){ this.get('logger')('Began job.') /* take a job and run it, add workers if needed */ } }); Need a logger var container = new Ember.Container(); container.register('workerPool:main', WorkerPool); var pool = container.lookup('workerPool:main'); pool.submitJob(job);
  • 47.
    Inject a dependencyon a factory 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var WorkerPool = Ember.Object.extend({ workers: /* an array of workers */, limit: 3, submitJob: function(job){ this.get('logger')('Began job.') /* take a job and run it, add workers if needed */ } }); var container = new Ember.Container(); container.register('workerPool:main', WorkerPool); container.register('logger:alert', window.alert, {instantiate: false}); container.injection('workerPool:main', 'logger', 'logger:alert'); var pool = container.lookup('workerPool:main'); pool.submitJob(job);
  • 48.
    Inject a dependencyon a factory 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var WorkerPool = Ember.Object.extend({ workers: /* an array of workers */, limit: 3, submitJob: function(job){ this.get('logger')('Began job.') /* take a job and run it, add workers if needed */ } }); var container = new Ember.Container(); container.register('workerPool:main', WorkerPool); container.register('logger:alert', window.alert, {instantiate: false}); container.injection('workerPool:main', 'logger', 'logger:alert'); var pool = container.lookup('workerPool:main'); pool.submitJob(job);
  • 49.
    Inject a dependencyon a factory 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var WorkerPool = Ember.Object.extend({ workers: /* an array of workers */, limit: 3, submitJob: function(job){ this.get('logger')('Began job.') /* take a job and run it, add workers if needed */ } }); var container = new Ember.Container(); container.register('workerPool:main', WorkerPool); container.register('logger:alert', window.alert, {instantiate: false}); container.injection('workerPool:main', 'logger', 'logger:alert'); var pool = container.lookup('workerPool:main'); pool.submitJob(job); control over the dependency
  • 50.
    Inject a dependencyon a type 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var WorkerPool = Ember.Object.extend({ workers: /* an array of workers */, limit: 3, submitJob: function(job){ this.get('logger')('Began job.') /* take a job and run it, add workers if needed */ } }); var BigPool = WorkerPool.extend({ limit: 10 }); var container = new Ember.Container(); container.register('workerPool:main', WorkerPool); container.register('workerPool:big', BigPool); container.register('logger:alert', window.alert, {instantiate: false}); container.injection('workerPool', 'logger', 'logger:alert'); var pool = container.lookup('workerPool:big'); pool.submitJob(job);
  • 51.
    Inject a dependencyon a type 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var WorkerPool = Ember.Object.extend({ workers: /* an array of workers */, limit: 3, submitJob: function(job){ this.get('logger')('Began job.') /* take a job and run it, add workers if needed */ } }); var BigPool = WorkerPool.extend({ limit: 10 }); var container = new Ember.Container(); container.register('workerPool:main', WorkerPool); container.register('workerPool:big', BigPool); container.register('logger:alert', window.alert, {instantiate: false}); container.injection('workerPool', 'logger', 'logger:alert'); var pool = container.lookup('workerPool:big'); pool.submitJob(job);
  • 52.
    Inject a dependencyon a type 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var WorkerPool = Ember.Object.extend({ workers: /* an array of workers */, limit: 3, submitJob: function(job){ this.get('logger')('Began job.') /* take a job and run it, add workers if needed */ } }); var BigPool = WorkerPool.extend({ limit: 10 }); var container = new Ember.Container(); container.register('workerPool:main', WorkerPool); container.register('workerPool:big', BigPool); container.register('logger:alert', window.alert, {instantiate: false}); container.injection('workerPool', 'logger', 'logger:alert'); var pool = container.lookup('workerPool:big'); pool.submitJob(job); control over the dependency
  • 53.
    a wild dependencyappears 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var WorkerPool = Ember.Object.extend({ workers: /* an array of workers */, limit: 3, submitJob: function(job){ this.get('logger')('Began job.') /* take a job and run it, add workers if needed */ } }); Need worker var BigPool = WorkerPool.extend({ limit: 10 }); var container = new Ember.Container(); container.register('workerPool:main', WorkerPool); container.register('workerPool:big', BigPool); container.register('logger:alert', window.alert, {instantiate: false}); container.injection('workerPool', 'logger', 'logger:alert'); var pool = container.lookup('workerPool:big'); pool.submitJob(job);
  • 54.
    Lookup a dependency 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 varWorker = Ember.Object.extend({ run: function(){ /* run a job */ } }); var WorkerPool = Ember.Object.extend({ workers: /* an array of workers */, noWorkerAvailable: /* check for a free worker */, limit: 3, submitJob: function(job){ if (this.get('noWorkerAvailable')) { var worker = this.container.lookup('worker:sync'); worker.run(job); this.get('workers').pushObject(worker); } // else find a free worker in the pool and run } }); var container = new Ember.Container(); container.register('workerPool:main', WorkerPool); container.register('worker:sync', Worker, {singleton: false}); var pool = container.lookup('workerPool:main'); pool.submitJob(job);
  • 55.
    Lookup a dependency 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 varWorker = Ember.Object.extend({ run: function(){ /* run a job */ } }); var WorkerPool = Ember.Object.extend({ workers: /* an array of workers */, noWorkerAvailable: /* check for a free worker */, limit: 3, submitJob: function(job){ if (this.get('noWorkerAvailable')) { var worker = this.container.lookup('worker:sync'); worker.run(job); this.get('workers').pushObject(worker); } // else find a free worker in the pool and run } }); var container = new Ember.Container(); container.register('workerPool:main', WorkerPool); container.register('worker:sync', Worker, {singleton: false}); var pool = container.lookup('workerPool:main'); pool.submitJob(job);
  • 56.
    Lookup a dependency 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 varWorker = Ember.Object.extend({ run: function(){ /* run a job */ } }); var WorkerPool = Ember.Object.extend({ workers: /* an array of workers */, noWorkerAvailable: /* check for a free worker */, limit: 3, submitJob: function(job){ if (this.get('noWorkerAvailable')) { var worker = this.container.lookup('worker:sync'); worker.run(job); this.get('workers').pushObject(worker); } // else find a free worker in the pool and run } }); control over the dependency var container = new Ember.Container(); container.register('workerPool:main', WorkerPool); container.register('worker:sync', Worker, {singleton: false}); var pool = container.lookup('workerPool:main'); pool.submitJob(job);
  • 57.
    dynamically Lookup adependency 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var WorkerPool = Ember.Object.extend({ workers: /* an array of workers */, noWorkerAvailable: /* check for a free worker */, limit: 3, submitJob: function(job){ if (this.get('noWorkerAvailable')) { var worker = this.container.lookup( 'worker:'+(job.get('isAsync') ? 'async' : 'sync') ); worker.run(job); this.get('workers').pushObject(worker); } // else find a free worker in the pool and run } }); var container = new Ember.Container(); container.register('workerPool:main', WorkerPool); container.register('worker:sync', Worker, {singleton: false}); container.register('worker:async', AsyncWorker, {singleton: false}); var pool = container.lookup('workerPool:main'); pool.submitJob(job);
  • 58.
    Injection places control outsidethe target, lookup makes it internal.
  • 59.
  • 60.
    resolve to namespace 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 functioncapitalize(str){ return str.charAt(0).toUpperCase() + str.slice(1); } var MyScope = {}; MyScope.SyncWorker = Ember.Object.extend({ run: function(){ /* run a job */ } }); var container = new Ember.Container(); container.resolve = function(name){ var parts = name.split(':'), className = capitalize(parts[1])+capitalize(parts[0]); return MyScope[className]; } var worker = container.lookup('worker:sync', {instantiate: false});
  • 61.
    resolve to AMDmodule 1 2 3 4 5 6 7 8 9 10 11 12 13 14 define('workers/sync', function(){ return Ember.Object.extend({ run: function(){ /* run a job */ } }); }); var container = new Ember.Container(); container.resolve = function(name){ var parts = name.split(':'), moduleName = parts[0]+'s/'+parts[1]; return require(moduleName); } var worker = container.lookup('worker:sync', {instantiate: false});
  • 62.
  • 63.
    Ember applications usea single container.
  • 64.
    Several ways toaccess it • Everything from a container has this.container
 1 App.IndexView = Ember.View.extend({ 2 router: function(){ 3 return this.container.lookup('router:main'); 4 }.property() 5 });
  • 65.
    Several ways toaccess it • Everything from a container has this.container
 router: function(){ return this.container.lookup('router:main'); }.property()! • App.register, App.Inject
 App.register('analytics:google', App.GoogleAnalytics);! App.inject('controller', 'analytics', 'analytics:google');!
  • 66.
    Several ways toaccess it • Everything from a container has this.container
 router: function(){ return this.container.lookup('router:main'); }.property()! • App.register, App.Inject
 App.register('analytics:google', App.GoogleAnalytics);! App.inject('controller', 'analytics', 'analytics:google');! • In Initializers App.initializer({ name: 'analytics', /* before: 'optionallyBeforeThis' */ initialize: function(container, application){ application.register('analytics:google', App.GoogleAnalytics); container.injection('controller', 'analytics', 'analytics:google'); } })
  • 67.
    Several ways toaccess it • Everything from a container has this.container
 router: function(){ return this.container.lookup('router:main'); }.property()! • App.register, App.Inject
 App.register('analytics:google', App.GoogleAnalytics);! App.inject('controller', 'analytics', 'analytics:google');! • In Initializers
 App.initializer({ initialize: function(container, application){ // ... • needs App.AnalyticsController = Ember.Controller.extend(); App.IndexController = Ember.Controller.extend({ needs: ['analytics'], analytics: Ember.computed.alias('controllers.analytics') }); !
  • 68.
    Several ways toaccess it • Everything from a container has this.container
 router: function(){ return this.container.lookup('router:main'); }.property()! • App.register, App.Inject
 App.register('analytics:google', App.GoogleAnalytics);! App.inject('controller', 'analytics', 'analytics:google');! • In Initializers
 App.initializer({ initialize: function(container, application){ // ... • needs App.AnalyticsController = Ember.Controller.extend(); App.IndexController = Ember.Controller.extend({ needs: ['analytics'], analytics: Ember.computed.alias('controllers.analytics') }); !
  • 69.
    …and one unsafeway. // Never, ever use this in application code App.__container__
  • 70.
  • 71.
    ember Finds atemplate 1 Ember.View = Ember.CoreView.extend({ 2 3 templateForName: function(name, type) { 4 if (!name) { return; } 5 Ember.assert("templateNames are not allowed to contain periods: "+name, name.indexOf('.') == 6 7 // the defaultContainer is deprecated 8 var container = this.container || (Ember.Container && Ember.Container.defaultContainer); 9 return container && container.lookup('template:' + name); 10 }, 11 // ... 12 }); view.js
  • 72.
    ember Finds atemplate 1 Ember.DefaultResolver = Ember.Object.extend({ 2 3 resolveTemplate: function(parsedName) { 4 var templateName = parsedName.fullNameWithoutType.replace(/./g, '/'); 5 6 if (Ember.TEMPLATES[templateName]) { 7 return Ember.TEMPLATES[templateName]; 8 } 9 10 templateName = decamelize(templateName); 11 if (Ember.TEMPLATES[templateName]) { 12 return Ember.TEMPLATES[templateName]; 13 } 14 }, 15 // ... 16 }); resolver.js
  • 73.
    ember decides togenerate a controller 1 Ember.Route = Ember.Object.extend(Ember.ActionHandler, { 2 3 setup: function(context, transition) { 4 var controllerName = this.controllerName || this.routeName, 5 controller = this.controllerFor(controllerName, true); 6 if (!controller) { 7 controller = this.generateController(controllerName, context); 8 } 9 10 // ... route.js
  • 74.
    ember decides togenerate a controller 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 Ember.generateControllerFactory = function(container, controllerName, context) {! var Factory, fullName, instance, name, factoryName, controllerType;! ! if (context && Ember.isArray(context)) {! controllerType = 'array';! } else if (context) {! controllerType = 'object';! } else {! controllerType = 'basic';! }! ! factoryName = 'controller:' + controllerType;! ! Factory = container.lookupFactory(factoryName).extend({! isGenerated: true,! toString: function() {! return "(generated " + controllerName + " controller)";! }! });! ! fullName = 'controller:' + controllerName;! ! container.register(fullName, Factory);! ! return Factory;! };! controller_for.js
  • 75.
    ember data injectsa store 1 Ember.onLoad('Ember.Application', function(Application) { 2 3 // ... adapters, serializers, transforms, etc 4 5 Application.initializer({ 6 name: "injectStore", 7 before: "store", 8 9 initialize: function(container, application) { 10 application.inject('controller', 'store', 'store:main'); 11 application.inject('route', 'store', 'store:main'); 12 application.inject('serializer', 'store', 'store:main'); 13 application.inject('dataAdapter', 'store', 'store:main'); 14 } 15 }); 16 17 }); initializers.js
  • 76.
  • 77.
    Make an litag active for sub routes (bootstrap nav) 1 App.ActiveLiComponent = Ember.Component.extend({ 2 tagName: 'li', 3 classNameBindings: ['isActive:active:inactive'], 4 5 router: function(){ 6 return this.container.lookup('router:main'); 7 }.property(), 8 9 isActive: function(){ 10 var currentWhen = this.get('currentWhen'); 11 return this.get('router').isActive(currentWhen); 12 }.property('router.url', 'currentWhen') 13 }); http://emberjs.jsbin.com/iFEvATE/2/edit?html,js,output
  • 78.
    Isolate tests witha container 1 var container, component, router;! 2 module("ActiveLiComponent tests", {! 3 setup: function(){! 4 container = new Ember.Container();! 5 container.register('component:active-li', App.ActiveLiComponent);! 6 container.register('router:main', Ember.Object.create({! 7 url: '/',! 8 isActive: function(){}! 9 }), {instantiate: false});! 10 component = container.lookup('component:active-li');! 11 router = container.lookup('router:main');! 12 },! 13 teardown: function() {! 14 Ember.run(container, 'destroy');! 15 }! 16 });! http://emberjs.jsbin.com/aGILoru/1/edit?js,output
  • 79.
    Create services withneeds 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 App.AudioPlayerController = Ember.Controller.extend({ play: function(track){ this.set('currentTrack', play); 1 {{! application.hbs }} this.set('isPlaying', true); 2 {{render “audio_player"}} }, 3 {{outlet}} // ... }); App.FieldElementController = Ember.Controller.extend({ needs: ['audio_player'], player: Ember.computed.alias('controllers.audio_player'), actions: { start: function(){ var player = this.get('player'), track = this.get('model.track'); player.play(track); } } }); http://to.be/fields/cG8QrM
  • 80.
    Isolate tests witha container 1 var container, controller, player; 2 module("FieldElementController tests", { 3 setup: function(){ 4 container = new Ember.Container(); 5 container.register('controller:field_element', App.FieldElement); 6 container.register('controller:audio_player', Ember.Object.extend({ 7 play: function(){} 8 })); 9 controller = container.lookup('controller:field_element'); 10 player = container.lookup('controller:audio_player'); 11 }, 12 teardown: function() { 13 Ember.run(container, 'destroy'); 14 } 15 });
  • 81.
  • 82.
    deferred queue uploader 1ToBe.initializer({ 2 name: 'fieldElementUpdater', 3 4 initialize: function(container, application){ 5 6 // Register the fieldElementUpdater to the controllers, models, views, routes. 7 container.optionsForType('fieldElementUpdater', {singleton: true}); 8 container.register('fieldElementUpdater:main', ToBe.FieldElementUpdater); 9 container.injection('controller', 'fieldElementUpdater', 'fieldElementUpdater:main'); 10 container.injection('model', 'fieldElementUpdater', 'fieldElementUpdater:main'); 11 container.injection('route', 'fieldElementUpdater', 'fieldElementUpdater:main'); 12 13 // Legacy, non-Ember code 14 application.set('fieldElementUpdater', container.lookup('fieldElementUpdater:main')); 15 16 } 17 }); http://to.be/fields/iJYzt-
  • 83.
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 Ember.Application.initializer({ name: 'adapter', before: 'authentication', initialize:function(container, app){ import FirebaseAdapter from 'appkit/adapters/firebase'; container.register('adapter:firebase', FirebaseAdapter); app.inject('controller', 'firebase', 'adapter:firebase'); app.inject('route', 'firebase', 'adapter:firebase'); } }); Ember.Application.initializer({ name: 'authentication', initialize: function(container, app){ import Session from 'appkit/controllers/session'; container.register('session:main', Session); app.inject('controller', 'session', 'session:main'); app.inject('route', 'session', 'session:main'); import FirebaseAuth from 'appkit/models/firebase_auth'; container.register('session:adapter', FirebaseAuth); app.inject('session:adapter', 'firebase', 'adapter:firebase'); app.inject('session:main', 'adapter', 'session:adapter'); } }); https://github.com/mixonic/grimvisage auth
  • 84.
    1 var SessionController= Ember.Object.extend({ 2 isAuthenticated: false, 3 currentUser: null, 4 afterRedirect: null, 5 6 open: function(credentials){ 7 var session = this; 8 return this.get('adapter').open(credentials, this) 9 .then(function(user){ 10 session.set('isAuthenticated', true); 11 session.set('currentUser', user); 12 return user; 13 }, Ember.RSVP.reject); 14 }, 15 16 fetch: function(){ 17 var session = this; 18 return this.get('adapter').fetch(this) 19 .then(function(user){ 20 session.set('isAuthenticated', true); 21 session.set('currentUser', user); 22 return user; 23 }, Ember.RSVP.reject); 24 }, 25 26 close: function(){ 27 var session = this; 28 return this.get('adapter').close(this) 29 .then(function(ref){ 30 session.set('isAuthenticated', false); 31 session.set('currentUser', null); 32 return ref; 33 }, Ember.RSVP.reject); 34 } 35 }); 36 37 export default SessionController; session.js auth
  • 85.
    Containers allow the definitionof patterns beyond MVC.
  • 86.
    The resolver andcontainer power Ember’s
 module-filled-future.
  • 87.