Dependency Injection into Ember.Object

I’m wondering if it’s possible to use Ember’s Dependency Injection to inject into Ember.Object? I’ve got a type of global data manager injected into controllers etc. just fine, but I’d like to use it from a couple methods of an object created with Ember.Object.extend.

The current public API for defining injections is limited to Application#register and Application#inject. All property injections happen via the rules defined by these two methods, which ends up limiting you to injecting properties into instances of pre-defined Ember objects. However, if you are feeling adventurous, Ember has a full-fledged container with an undocumented API that you can use to accomplish what you are describing. It is not currently considered final, so you run the risk of things breaking when upgrading to future versions of Ember. That said, what you’ll need to use is highly unlikely to change anytime soon IMO. You’ll need to do a few things (you may already be familiar with steps 1-3):

  1. Register your factory (Ember.Object subclass) with the container by giving it a name:
App.register('thing:main', Ember.Object.extend({ ... }), { singleton: false });

All factories have a ‘type’, so we’re making up a new one here called thing. When there’s only one factory for a particular type, the convention is to give it the name main (Ember Data does the same thing with store:main). The singleton: false option ensures that when each time we create an object later, we get a new instance instead of the same one.

  1. Register whatever it is you are going to inject into all instances of your class with the container:
App.register('global:data-manager', DataManager);

This step has to happen because the container cannot inject something it doesn’t know about. Usually what you want injected into new objects is a singleton instance of a factory, and currently there’s a container bug preventing injections from being non-singletons. If what you are injecting is already registered with the container, e.g. store:main, you can skip this step.

  1. Define an injection onto all instances of your class:
App.inject('thing:main', 'dataManager', 'global:data-manager');

This defines an injection rule that translates to: “Set the dataManager property of all new instances of thing:main to a singleton instance of global:data-manager

  1. Create instances of your class through the container:
var thing = this.container.lookup('thing:main');

This is the part that uses the undocumented API. Instances of your class must be created by the container, because it’s what actually does the injecting. This is how Ember creates instances of almost all objects internally (routes, controllers, views, etc.). So first we’ll need a reference to the container, which is usually not an issue because all objecs created through the container have a reference to the container through a container property. That means that the above snippet will work inside all routes, controllers, and views (but not models by default). Then we simply create an new instance by ‘looking up’ the factory. :tada:

Side note: you may also be interested in what’s going on with Ember ‘services’.

7 Likes

Brilliant! That’s exactly what I was looking for! Thank you very much for the clear instructions.

Have there been any updates to this since July 2014, or at least any alternatives?

Our code base relies on a large set of Ember objects that all make their own server calls. We’re using Ember’s new service concept to build a generic way of speaking with our server, and I would like to see if there is a way of giving access to that service to all of the relevant Ember objects without necessarily relying on non public API.

The approach described above (using register and inject) is still the way to go. The syntax has changed somewhat but the basic concepts haven’t changed much. There are now public APIs for interacting with the ember.js registry and container - see Ember.js 2.1 Beta Released

I have created es6 modules that export a singleton Ember object for cases where the service needs to be available everywhere. Any other module can import that and even observe with some glue code. Not a real service of course but something similar.