containers & dependency in ember.js
DESCRIPTION
Ember applications are built around an MVC model that prescribes Models, Views, Controllers, and Routes for managing persistence, DOM, application state, and URLs. In an ambitious enough app, that model may fail to cover the whole problem space. Fear not, for in this presentation you will learn how to use Ember’s container and dependency features to move beyond MVC, as well as learning how they tie Ember internals together. Video: http://www.youtube.com/watch?v=iCZUKFNXA0k&feature=youtu.be&t=1h45m59sTRANSCRIPT
Hi. I’m Matthew.
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
• Models • Views • Controllers
• MVC FRamework
• MVC FRamework
• Models • Views • Controllers
• Components • ROUTES • Router • templates
• Components • ROUTES • Router • templates
• MVC FRamework
• Models • Views • Controllers
• Store • Serializers • adapters
SO MANY THINGS
What makes arouter different from a template?
What makes arouter different from a template?
• maps routes to states • only one router • instance of a class • App.Router • Has access to store (ED)
What makes arouter 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 App.IndexView = Ember.View.extend({ 2 click: function(){ alert('click!'); } 3 });
1 App.WidgetView = Ember.View.extend({ 2 click: function(){ alert('widget!'); } 3 });
1 Ember.View = Ember.Object.extend({ 2 appendTo: function(){ /* ... */ }, 3 render: function(){ /* ... */ }, 4 rerender: function(){ /* ... */ } 5 });
What makes things similar?
• Shared Interface (superclass?)App.IndexView = Ember.View.extend({ // ...
What makes things similar?
• Shared Interface (superclass?)
• SHARED DEPENDENCIES
App.IndexView = Ember.View.extend({ // ...
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?)
• SHARED DEPENDENCIESthis.get('router'); // In a route
App.IndexView = Ember.View.extend({ // ...
What makes things similar?
• Shared Interface (superclass?)
• SHARED DEPENDENCIES
• SHAREd USAGEthis.get('router'); // In a route
App.IndexView = Ember.View.extend({ // ...
Ember.Component.extend({ !!Ember.Controller.extend({ !!Ember.Handlebars.compile(" !
Ember.Component.extend({ !!Ember.Controller.extend({ !!Ember.Handlebars.compile(" !
Is A !!Class !!Class !!Function
Ember.Component.extend({ !!Ember.Controller.extend({ !!Ember.Handlebars.compile(" !
Is A !!Class !!Class !!Function
Instantiate? !!!!!!!!!
✓✓✗
Ember.Component.extend({ !!Ember.Controller.extend({ !!Ember.Handlebars.compile(" !
Is A !!Class !!Class !!Function
Instantiate? !!!!!!!!!
New every time? !!!!!!!!!
✓✓✗ ✗
✗
✓
What makes things similar?
• Shared Interface (superclass?)
• SHARED DEPENDENCIES
• SHAREd USAGEthis.get('router'); // In a route
Instantiate all the controllers, but return the same one each time
App.IndexView = Ember.View.extend({ // ...
What makes things similar?
• Shared Interface (superclass?)
• SHARED DEPENDENCIES
• SHAREd USAGE
• SHARED DISCOVERY
this.get('router'); // In a route
Instantiate all the controllers, but return the same one each time
App.IndexView = Ember.View.extend({ // ...
What makes things similar?
• Shared Interface (superclass?)
• SHARED DEPENDENCIES
• SHAREd USAGE
• SHARED DISCOVERY
this.get('router'); // In a route
Instantiate all the controllers, but return the same one each time
App.ColorPickerComponent Ember.TEMPLATES['index'] App.Car
App.IndexView = Ember.View.extend({ // ...
What makes things similar?
• Shared Interface (superclass?)
• SHARED DEPENDENCIES
• SHAREd USAGE
• SHARED DISCOVERY
this.get('router'); // In a route
Instantiate all the controllers, but return the same one each time
App.ColorPickerComponent Ember.TEMPLATES['index'] App.Car
App.IndexView = Ember.View.extend({ // ...
What makes things similar?
• Shared Interface (superclass?)
• SHARED DEPENDENCIES
• SHAREd USAGE
• SHARED DISCOVERY
this.get('router'); // In a route
Instantiate all the controllers, but return the same one each time
App.ColorPickerComponent Ember.TEMPLATES['index'] App.Car
App.IndexView = Ember.View.extend({ // ...
CONTAINERS
The container organizes Ember’s building blocks.
The container re-organizes Ember’s building blocks.
The container organizes new building blocks.
register/lookup
1 var WorkerPool = Ember.Object.extend({ 2 workers: /* an array of workers */, 3 limit: 3, 4 submitJob: /* take a job and run it, add workers if needed */ 5 }); 6 7 var container = new Ember.Container(); 8 9 container.register('workerPool:main', WorkerPool); 10 11 container.lookup('workerPool:main'); // -> Instance of WorkerPool 12 container.lookup('workerPool:main'); // -> Same instance of WorkerPool
register a factory into a container
1 var WorkerPool = Ember.Object.extend({ 2 workers: /* an array of workers */, 3 limit: 3, 4 submitJob: /* take a job and run it, add workers if needed */ 5 }); 6 7 var container = new Ember.Container(); 8 9 container.register('workerPool:main', WorkerPool); 10 11 container.lookup('workerPool:main'); // -> Instance of WorkerPool 12 container.lookup('workerPool:main'); // -> Same instance of WorkerPool
register a factory into a container
singleton (always the same instance)
1 var WorkerPool = Ember.Object.extend({ 2 workers: /* an array of workers */, 3 limit: 3, 4 submitJob: /* take a job and run it, add workers if needed */ 5 }); 6 7 var BigPool = WorkerPool.extend({ limit: 10 }); 8 9 var container = new Ember.Container(); 10 11 container.register('workerPool:main', WorkerPool); 12 container.register('workerPool:big', BigPool); 13 14 container.lookup('workerPool:main'); // -> Instance of WorkerPool 15 container.lookup('workerPool:big'); // -> Instance of BigPool
register like factories into a container
1 var WorkerPool = Ember.Object.extend({ 2 workers: /* an array of workers */, 3 limit: 3, 4 submitJob: /* take a job and run it, add workers if needed */ 5 }); 6 7 var BigPool = WorkerPool.extend({ limit: 10 }); 8 9 var container = new Ember.Container(); 10 11 container.register('workerPool:main', BigPool); 12 container.register('workerPool:big', BigPool); 13 14 container.lookup('workerPool:main'); // -> Instance of BigPool 15 container.lookup('workerPool:big'); // -> Different instance of BigPool
register like factories into a container
register non-singleton factories into a container
1 var Worker = Ember.Object.extend({ 2 run: function(){ /* run a job */ } 3 }); 4 5 var container = new Ember.Container(); 6 7 container.register('worker:sync', Worker, {singleton: false}); 8 9 container.lookup('worker:sync'); // -> Instance of Worker 10 container.lookup('worker:sync'); // -> New instance of Worker 11 container.lookup('worker:sync'); // -> New instance of Worker!
register non-class factories into a container
1 var container = new Ember.Container(); 2 3 container.register('logger:alert', window.alert, {instantiate: false}); 4 5 container.register('logger:console', function(msg){ 6 window.console.log(msg); 7 }, {instantiate: false}); 8 9 container.lookup('logger:alert'); // -> alert function 10 container.lookup('logger:console'); // -> console.log function 11 container.lookup('logger:console')('Howdy!'); // -> "Howdy!"
injection + dependency lookup
a wild dependency appears
1 var WorkerPool = Ember.Object.extend({ 2 workers: /* an array of workers */, 3 limit: 3, 4 submitJob: function(job){ 5 this.get('logger')('Began job.') 6 /* take a job and run it, add workers if needed */ 7 } 8 }); 9 10 var container = new Ember.Container(); 11 container.register('workerPool:main', WorkerPool); 12 13 var pool = container.lookup('workerPool:main'); 14 pool.submitJob(job);
a wild dependency appears
1 var WorkerPool = Ember.Object.extend({ 2 workers: /* an array of workers */, 3 limit: 3, 4 submitJob: function(job){ 5 this.get('logger')('Began job.') 6 /* take a job and run it, add workers if needed */ 7 } 8 }); 9 10 var container = new Ember.Container(); 11 container.register('workerPool:main', WorkerPool); 12 13 var pool = container.lookup('workerPool:main'); 14 pool.submitJob(job);
Need a logger
Inject a dependency on a factory
1 var WorkerPool = Ember.Object.extend({ 2 workers: /* an array of workers */, 3 limit: 3, 4 submitJob: function(job){ 5 this.get('logger')('Began job.') 6 /* take a job and run it, add workers if needed */ 7 } 8 }); 9 10 var container = new Ember.Container(); 11 container.register('workerPool:main', WorkerPool); 12 container.register('logger:alert', window.alert, {instantiate: false}); 13 14 container.injection('workerPool:main', 'logger', 'logger:alert'); 15 16 var pool = container.lookup('workerPool:main'); 17 pool.submitJob(job);
Inject a dependency on a factory
1 var WorkerPool = Ember.Object.extend({ 2 workers: /* an array of workers */, 3 limit: 3, 4 submitJob: function(job){ 5 this.get('logger')('Began job.') 6 /* take a job and run it, add workers if needed */ 7 } 8 }); 9 10 var container = new Ember.Container(); 11 container.register('workerPool:main', WorkerPool); 12 container.register('logger:alert', window.alert, {instantiate: false}); 13 14 container.injection('workerPool:main', 'logger', 'logger:alert'); 15 16 var pool = container.lookup('workerPool:main'); 17 pool.submitJob(job);
Inject a dependency on a factory
1 var WorkerPool = Ember.Object.extend({ 2 workers: /* an array of workers */, 3 limit: 3, 4 submitJob: function(job){ 5 this.get('logger')('Began job.') 6 /* take a job and run it, add workers if needed */ 7 } 8 }); 9 10 var container = new Ember.Container(); 11 container.register('workerPool:main', WorkerPool); 12 container.register('logger:alert', window.alert, {instantiate: false}); 13 14 container.injection('workerPool:main', 'logger', 'logger:alert'); 15 16 var pool = container.lookup('workerPool:main'); 17 pool.submitJob(job);
control over the dependency
Inject a dependency on a type
1 var WorkerPool = Ember.Object.extend({ 2 workers: /* an array of workers */, 3 limit: 3, 4 submitJob: function(job){ 5 this.get('logger')('Began job.') 6 /* take a job and run it, add workers if needed */ 7 } 8 }); 9 10 var BigPool = WorkerPool.extend({ limit: 10 }); 11 12 var container = new Ember.Container(); 13 container.register('workerPool:main', WorkerPool); 14 container.register('workerPool:big', BigPool); 15 container.register('logger:alert', window.alert, {instantiate: false}); 16 17 container.injection('workerPool', 'logger', 'logger:alert'); 18 19 var pool = container.lookup('workerPool:big'); 20 pool.submitJob(job);
Inject a dependency on a type
1 var WorkerPool = Ember.Object.extend({ 2 workers: /* an array of workers */, 3 limit: 3, 4 submitJob: function(job){ 5 this.get('logger')('Began job.') 6 /* take a job and run it, add workers if needed */ 7 } 8 }); 9 10 var BigPool = WorkerPool.extend({ limit: 10 }); 11 12 var container = new Ember.Container(); 13 container.register('workerPool:main', WorkerPool); 14 container.register('workerPool:big', BigPool); 15 container.register('logger:alert', window.alert, {instantiate: false}); 16 17 container.injection('workerPool', 'logger', 'logger:alert'); 18 19 var pool = container.lookup('workerPool:big'); 20 pool.submitJob(job);
Inject a dependency on a type
1 var WorkerPool = Ember.Object.extend({ 2 workers: /* an array of workers */, 3 limit: 3, 4 submitJob: function(job){ 5 this.get('logger')('Began job.') 6 /* take a job and run it, add workers if needed */ 7 } 8 }); 9 10 var BigPool = WorkerPool.extend({ limit: 10 }); 11 12 var container = new Ember.Container(); 13 container.register('workerPool:main', WorkerPool); 14 container.register('workerPool:big', BigPool); 15 container.register('logger:alert', window.alert, {instantiate: false}); 16 17 container.injection('workerPool', 'logger', 'logger:alert'); 18 19 var pool = container.lookup('workerPool:big'); 20 pool.submitJob(job);
control over the dependency
1 var WorkerPool = Ember.Object.extend({ 2 workers: /* an array of workers */, 3 limit: 3, 4 submitJob: function(job){ 5 this.get('logger')('Began job.') 6 /* take a job and run it, add workers if needed */ 7 } 8 }); 9 10 var BigPool = WorkerPool.extend({ limit: 10 }); 11 12 var container = new Ember.Container(); 13 container.register('workerPool:main', WorkerPool); 14 container.register('workerPool:big', BigPool); 15 container.register('logger:alert', window.alert, {instantiate: false}); 16 17 container.injection('workerPool', 'logger', 'logger:alert'); 18 19 var pool = container.lookup('workerPool:big'); 20 pool.submitJob(job);
Need worker
a wild dependency appears
Lookup a dependency 1 var Worker = Ember.Object.extend({ 2 run: function(){ /* run a job */ } 3 }); 4 5 var WorkerPool = Ember.Object.extend({ 6 workers: /* an array of workers */, 7 noWorkerAvailable: /* check for a free worker */, 8 limit: 3, 9 submitJob: function(job){ 10 if (this.get('noWorkerAvailable')) { 11 var worker = this.container.lookup('worker:sync'); 12 worker.run(job); 13 this.get('workers').pushObject(worker); 14 } // else find a free worker in the pool and run 15 } 16 }); 17 18 var container = new Ember.Container(); 19 container.register('workerPool:main', WorkerPool); 20 container.register('worker:sync', Worker, {singleton: false}); 21 22 var pool = container.lookup('workerPool:main'); 23 pool.submitJob(job);
Lookup a dependency 1 var Worker = Ember.Object.extend({ 2 run: function(){ /* run a job */ } 3 }); 4 5 var WorkerPool = Ember.Object.extend({ 6 workers: /* an array of workers */, 7 noWorkerAvailable: /* check for a free worker */, 8 limit: 3, 9 submitJob: function(job){ 10 if (this.get('noWorkerAvailable')) { 11 var worker = this.container.lookup('worker:sync'); 12 worker.run(job); 13 this.get('workers').pushObject(worker); 14 } // else find a free worker in the pool and run 15 } 16 }); 17 18 var container = new Ember.Container(); 19 container.register('workerPool:main', WorkerPool); 20 container.register('worker:sync', Worker, {singleton: false}); 21 22 var pool = container.lookup('workerPool:main'); 23 pool.submitJob(job);
Lookup a dependency 1 var Worker = Ember.Object.extend({ 2 run: function(){ /* run a job */ } 3 }); 4 5 var WorkerPool = Ember.Object.extend({ 6 workers: /* an array of workers */, 7 noWorkerAvailable: /* check for a free worker */, 8 limit: 3, 9 submitJob: function(job){ 10 if (this.get('noWorkerAvailable')) { 11 var worker = this.container.lookup('worker:sync'); 12 worker.run(job); 13 this.get('workers').pushObject(worker); 14 } // else find a free worker in the pool and run 15 } 16 }); 17 18 var container = new Ember.Container(); 19 container.register('workerPool:main', WorkerPool); 20 container.register('worker:sync', Worker, {singleton: false}); 21 22 var pool = container.lookup('workerPool:main'); 23 pool.submitJob(job);
control over the dependency
dynamically Lookup a dependency 1 var WorkerPool = Ember.Object.extend({ 2 workers: /* an array of workers */, 3 noWorkerAvailable: /* check for a free worker */, 4 limit: 3, 5 submitJob: function(job){ 6 if (this.get('noWorkerAvailable')) { 7 var worker = this.container.lookup( 8 'worker:'+(job.get('isAsync') ? 'async' : 'sync') 9 ); 10 worker.run(job); 11 this.get('workers').pushObject(worker); 12 } // else find a free worker in the pool and run 13 } 14 }); 15 16 var container = new Ember.Container(); 17 container.register('workerPool:main', WorkerPool); 18 container.register('worker:sync', Worker, {singleton: false}); 19 container.register('worker:async', AsyncWorker, {singleton: false}); 20 21 var pool = container.lookup('workerPool:main'); 22 pool.submitJob(job);
Injection places control outside the target, lookup
makes it internal.
resolving factories
resolve to namespace
1 function capitalize(str){ 2 return str.charAt(0).toUpperCase() + str.slice(1); 3 } 4 5 var MyScope = {}; 6 MyScope.SyncWorker = Ember.Object.extend({ 7 run: function(){ /* run a job */ } 8 }); 9 10 var container = new Ember.Container(); 11 container.resolve = function(name){ 12 var parts = name.split(':'), 13 className = capitalize(parts[1])+capitalize(parts[0]); 14 return MyScope[className]; 15 } 16 17 var worker = container.lookup('worker:sync', {instantiate: false});
resolve to AMD module
1 define('workers/sync', function(){ 2 return Ember.Object.extend({ 3 run: function(){ /* run a job */ } 4 }); 5 }); 6 7 var container = new Ember.Container(); 8 container.resolve = function(name){ 9 var parts = name.split(':'), 10 moduleName = parts[0]+'s/'+parts[1]; 11 return require(moduleName); 12 } 13 14 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
• App.register, App.Injectrouter: function(){ return this.container.lookup('router:main'); }.property()!
App.register('analytics:google', App.GoogleAnalytics);!App.inject('controller', 'analytics', 'analytics:google');!
Several ways to access it
• Everything from a container has this.container
• App.register, App.Inject
• In Initializers
router: function(){ return this.container.lookup('router:main'); }.property()!
App.register('analytics:google', App.GoogleAnalytics);!App.inject('controller', 'analytics', 'analytics:google');!
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
• App.register, App.Inject
• In Initializers
• needs
router: function(){ return this.container.lookup('router:main'); }.property()!
App.register('analytics:google', App.GoogleAnalytics);!App.inject('controller', 'analytics', 'analytics:google');!
App.initializer({ initialize: function(container, application){ // ...
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
• App.register, App.Inject
• In Initializers
• needs
router: function(){ return this.container.lookup('router:main'); }.property()!
App.register('analytics:google', App.GoogleAnalytics);!App.inject('controller', 'analytics', 'analytics:google');!
App.initializer({ initialize: function(container, application){ // ...
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('.') === -1); 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
route.js
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 // ...
1 Ember.generateControllerFactory = function(container, controllerName, context) {! 2 var Factory, fullName, instance, name, factoryName, controllerType;! 3 ! 4 if (context && Ember.isArray(context)) {! 5 controllerType = 'array';! 6 } else if (context) {! 7 controllerType = 'object';! 8 } else {! 9 controllerType = 'basic';!10 }!11 !12 factoryName = 'controller:' + controllerType;!13 !14 Factory = container.lookupFactory(factoryName).extend({!15 isGenerated: true,!16 toString: function() {!17 return "(generated " + controllerName + " controller)";!18 }!19 });!20 !21 fullName = 'controller:' + controllerName;!22 !23 container.register(fullName, Factory);!24 !25 return Factory;!26 };!
ember decides to generate a controller
controller_for.js
ember data injects a store
initializers.js
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 });
How to re-organzie types
Make an li tag active for sub routes (bootstrap nav)
http://emberjs.jsbin.com/iFEvATE/2/edit?html,js,output
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 });
Isolate tests with a container
http://emberjs.jsbin.com/aGILoru/1/edit?js,output
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 });!
1 App.AudioPlayerController = Ember.Controller.extend({ 2 play: function(track){ 3 this.set('currentTrack', play); 4 this.set('isPlaying', true); 5 }, 6 // ... 7 }); 8 9 App.FieldElementController = Ember.Controller.extend({ 10 needs: ['audio_player'], 11 player: Ember.computed.alias('controllers.audio_player'), 12 actions: { 13 start: function(){ 14 var player = this.get('player'), 15 track = this.get('model.track'); 16 player.play(track); 17 } 18 } 19 });
Create services with needs
1 {{! application.hbs }} 2 {{render “audio_player"}} 3 {{outlet}}
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
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 });
deferred queue uploader
http://to.be/fields/iJYzt-
1 Ember.Application.initializer({ 2 name: 'adapter', 3 before: 'authentication', 4 initialize: function(container, app){ 5 6 import FirebaseAdapter from 'appkit/adapters/firebase'; 7 container.register('adapter:firebase', FirebaseAdapter); 8 app.inject('controller', 'firebase', 'adapter:firebase'); 9 app.inject('route', 'firebase', 'adapter:firebase'); 10 } 11 }); 12 Ember.Application.initializer({ 13 name: 'authentication', 14 initialize: function(container, app){ 15 16 import Session from 'appkit/controllers/session'; 17 container.register('session:main', Session); 18 app.inject('controller', 'session', 'session:main'); 19 app.inject('route', 'session', 'session:main'); 20 21 import FirebaseAuth from 'appkit/models/firebase_auth'; 22 container.register('session:adapter', FirebaseAuth); 23 app.inject('session:adapter', 'firebase', 'adapter:firebase'); 24 app.inject('session:main', 'adapter', 'session:adapter'); 25 } 26 });
auth
https://github.com/mixonic/grimvisage
auth
session.js
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;
Containers allow the definition of patterns
beyond MVC.
The resolver and container power Ember’s
module-filled-future.
Thanks!
@mixonic
httP://madhatted.com