SlideShare a Scribd company logo
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

More Related Content

PDF
Testing Ember Apps: Managing Dependency
PDF
Ember and containers
PPTX
Good karma: UX Patterns and Unit Testing in Angular with Karma
PDF
Ember testing internals with ember cli
PDF
Callbacks, promises, generators - asynchronous javascript
PDF
Django Celery - A distributed task queue
PDF
Asynchronous programming done right - Node.js
PDF
Complex Architectures in Ember
Testing Ember Apps: Managing Dependency
Ember and containers
Good karma: UX Patterns and Unit Testing in Angular with Karma
Ember testing internals with ember cli
Callbacks, promises, generators - asynchronous javascript
Django Celery - A distributed task queue
Asynchronous programming done right - Node.js
Complex Architectures in Ember

What's hot (20)

PPTX
Workshop 1: Good practices in JavaScript
PPTX
Intro to Ember.JS 2016
PDF
JavaScript Promise
PDF
An Introduction to Celery
PDF
Workshop 23: ReactJS, React & Redux testing
PPTX
Avoiding callback hell in Node js using promises
PPT
Testing Javascript with Jasmine
PPTX
The road to Ember.js 2.0
PDF
Workshop 5: JavaScript testing
PPTX
Avoiding Callback Hell with Async.js
ODP
Europython 2011 - Playing tasks with Django & Celery
PDF
Jasmine - why JS tests don't smell fishy
PDF
Practical Celery
PDF
Understanding Asynchronous JavaScript
PDF
Automated Testing in EmberJS
PPTX
Test like a pro with Ember.js
PDF
Workshop 26: React Native - The Native Side
PDF
Promise pattern
PDF
Why Task Queues - ComoRichWeb
KEY
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Workshop 1: Good practices in JavaScript
Intro to Ember.JS 2016
JavaScript Promise
An Introduction to Celery
Workshop 23: ReactJS, React & Redux testing
Avoiding callback hell in Node js using promises
Testing Javascript with Jasmine
The road to Ember.js 2.0
Workshop 5: JavaScript testing
Avoiding Callback Hell with Async.js
Europython 2011 - Playing tasks with Django & Celery
Jasmine - why JS tests don't smell fishy
Practical Celery
Understanding Asynchronous JavaScript
Automated Testing in EmberJS
Test like a pro with Ember.js
Workshop 26: React Native - The Native Side
Promise pattern
Why Task Queues - ComoRichWeb
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Ad

Similar to Containers & Dependency in Ember.js (20)

PDF
Workshop 16: EmberJS Parte I
PDF
Ember presentation
PPT
Ember.js: Jump Start
PPTX
Introduction to Ember.js
PPTX
Ember - introduction
PDF
Riding the Edge with Ember.js
PPTX
Intro to EmberJS
PDF
A Beginner's Guide to Ember
ODP
Introduction to ember js
PDF
Create an application with ember
PPTX
EmberJS BucharestJS
PDF
Intro to ember.js
PDF
Ember.js for a CakePHP Developer
PDF
The road to Ember 2.0
PDF
A Debugging Adventure: Journey through Ember.js Glue
PPTX
Introduction to Ember.js
PPTX
Getting into ember.js
PDF
Ember.js Meetup Brussels 31/10/2013
PDF
Ember vs Backbone
PDF
The Ember.js Framework - Everything You Need To Know
Workshop 16: EmberJS Parte I
Ember presentation
Ember.js: Jump Start
Introduction to Ember.js
Ember - introduction
Riding the Edge with Ember.js
Intro to EmberJS
A Beginner's Guide to Ember
Introduction to ember js
Create an application with ember
EmberJS BucharestJS
Intro to ember.js
Ember.js for a CakePHP Developer
The road to Ember 2.0
A Debugging Adventure: Journey through Ember.js Glue
Introduction to Ember.js
Getting into ember.js
Ember.js Meetup Brussels 31/10/2013
Ember vs Backbone
The Ember.js Framework - Everything You Need To Know
Ad

More from Matthew Beale (12)

PDF
Ember.js Module Loading
PDF
LA Ember.js Meetup, Jan 2017
PDF
Interoperable Component Patterns
PDF
Ember Community 2016 - Be the Bark
PDF
Attribute actions
PDF
The Hidden Power of HTMLBars (or, Scope in Ember.js Templates)
PDF
Aligning Ember.js with Web Standards
PDF
New Component Patterns in Ember.js
PDF
Scalable vector ember
PDF
Parse Apps with Ember.js
PDF
Snappy Means Happy: Performance in Ember Apps
PDF
Client-side Auth with Ember.js
Ember.js Module Loading
LA Ember.js Meetup, Jan 2017
Interoperable Component Patterns
Ember Community 2016 - Be the Bark
Attribute actions
The Hidden Power of HTMLBars (or, Scope in Ember.js Templates)
Aligning Ember.js with Web Standards
New Component Patterns in Ember.js
Scalable vector ember
Parse Apps with Ember.js
Snappy Means Happy: Performance in Ember Apps
Client-side Auth with Ember.js

Recently uploaded (20)

PPT
What is a Computer? Input Devices /output devices
PDF
Web App vs Mobile App What Should You Build First.pdf
PDF
Developing a website for English-speaking practice to English as a foreign la...
PDF
1 - Historical Antecedents, Social Consideration.pdf
PDF
WOOl fibre morphology and structure.pdf for textiles
PDF
Hindi spoken digit analysis for native and non-native speakers
PDF
Univ-Connecticut-ChatGPT-Presentaion.pdf
PDF
2021 HotChips TSMC Packaging Technologies for Chiplets and 3D_0819 publish_pu...
PDF
How ambidextrous entrepreneurial leaders react to the artificial intelligence...
PPTX
Tartificialntelligence_presentation.pptx
PPTX
TLE Review Electricity (Electricity).pptx
PDF
STKI Israel Market Study 2025 version august
PDF
Microsoft Solutions Partner Drive Digital Transformation with D365.pdf
PDF
TrustArc Webinar - Click, Consent, Trust: Winning the Privacy Game
PDF
DASA ADMISSION 2024_FirstRound_FirstRank_LastRank.pdf
PPTX
1. Introduction to Computer Programming.pptx
PDF
Getting started with AI Agents and Multi-Agent Systems
PDF
DP Operators-handbook-extract for the Mautical Institute
PDF
NewMind AI Weekly Chronicles – August ’25 Week III
PPTX
OMC Textile Division Presentation 2021.pptx
What is a Computer? Input Devices /output devices
Web App vs Mobile App What Should You Build First.pdf
Developing a website for English-speaking practice to English as a foreign la...
1 - Historical Antecedents, Social Consideration.pdf
WOOl fibre morphology and structure.pdf for textiles
Hindi spoken digit analysis for native and non-native speakers
Univ-Connecticut-ChatGPT-Presentaion.pdf
2021 HotChips TSMC Packaging Technologies for Chiplets and 3D_0819 publish_pu...
How ambidextrous entrepreneurial leaders react to the artificial intelligence...
Tartificialntelligence_presentation.pptx
TLE Review Electricity (Electricity).pptx
STKI Israel Market Study 2025 version august
Microsoft Solutions Partner Drive Digital Transformation with D365.pdf
TrustArc Webinar - Click, Consent, Trust: Winning the Privacy Game
DASA ADMISSION 2024_FirstRound_FirstRank_LastRank.pdf
1. Introduction to Computer Programming.pptx
Getting started with AI Agents and Multi-Agent Systems
DP Operators-handbook-extract for the Mautical Institute
NewMind AI Weekly Chronicles – August ’25 Week III
OMC Textile Division Presentation 2021.pptx

Containers & Dependency in Ember.js

  • 3. 201 Created We build õ-age apps with Ember.js. We take teams from £ to • in no time flat.
  • 5. WHAT IS EMBER MADE OF?
  • 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
  • 11. What makes a
 router different from a template?
  • 12. 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)
  • 13. 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
  • 14. What makes things similar?
  • 15. What makes things similar? • 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 things similar? • Shared Interface (superclass?) App.IndexView = Ember.View.extend({ // ...
  • 20. What makes things similar? • 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 things similar? • Shared Interface (superclass?)
 App.IndexView = Ember.View.extend({ // ... • SHARED DEPENDENCIES this.get('router'); // In a route
  • 23. What makes things similar? • Shared Interface (superclass?)
 App.IndexView = Ember.View.extend({ // ... • SHARED DEPENDENCIES
 this.get('router'); // In a route • SHAREd USAGE

  • 27. Is A Ember.Component.extend({ ! ! Ember.Controller.extend({ ! ! Ember.Handlebars.compile(" ! Instantiate? New every time? ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! Class ! ! Class ! ! Function ✓ ✓ ✗ ✓ ✗ ✗
  • 28. 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
  • 29. 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
  • 30. 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
  • 31. 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
  • 32. 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
  • 36. The container organizes new building blocks.
  • 38. 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
  • 39. 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)
  • 40. 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
  • 41. 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
  • 42. 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!
  • 43. 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!"
  • 45. 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);
  • 46. 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);
  • 47. 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);
  • 48. 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);
  • 49. 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
  • 50. 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);
  • 51. 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);
  • 52. 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
  • 53. 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);
  • 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 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);
  • 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 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);
  • 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 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);
  • 57. 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);
  • 58. Injection places control outside the target, lookup makes it internal.
  • 60. 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});
  • 61. 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});
  • 63. Ember applications use a single container.
  • 64. 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 });
  • 65. 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');!
  • 66. 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'); } })
  • 67. 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') }); !
  • 68. 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') }); !
  • 69. …and one unsafe way. // Never, ever use this in application code App.__container__
  • 71. 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
  • 72. 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
  • 73. 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
  • 74. 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
  • 75. 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
  • 77. 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
  • 78. 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
  • 79. 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
  • 80. 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 });
  • 82. 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-
  • 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 definition of patterns beyond MVC.
  • 86. The resolver and container power Ember’s
 module-filled-future.