dmathieu - Using EmberJS dependency injection

Using EmberJS dependency injection

Tuesday, 11 February 2014 in Development Articles by Damien Mathieu Creative Commons License

I’ve got a quite small personnal project using EmberJS with an API.
I’m using this project mostly as a sandbox of things to try out. So it probably won’t ever be public.

One of the things I’m trying to achieve with this project is completely decouple the API and the EmberJS app.
They are two different apps, running on two different hosts. And the EmberJS app has no access to any data except through the API.
One of the difficulties this posed was to manage user authentication.

Very basically, when authenticating, we’re creating a new instance of the Session model.
It’s data are stored in localStorage for retrieval after reloading the page.

This means I have a SessionStore object, which does the retrieval from localStorage and signs the user in if we already have a valid API token.

Quick and dirty solution

At first, I did reinvent the wheel a little bit with something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
App.CurrentSession = Ember.Object.extend
  id:    Ember.computed.alias('content.id')
  token: Ember.computed.alias('content.token')
  email: Ember.computed.alias('content.email')

  content: (->
    json = App.localStorage.getItem('session')
    data = JSON.parse(json) if json?

    if data?.id? && data?.token?
  ).property()

App.CurrentSession.reopenClass
  loadedSession: null

  load: ->
    new Ember.RSVP.Promise (resolve, reject) ->
      session = App.CurrentSession.loadedSession
      if !session?
        session = App.CurrentSession.create()
        App.CurrentSession.loadedSession = session
      Ember.run null, resolve, session

Which can then be used in any part of my application like this:

1
2
App.CurrentSession.load().then (session) ->
  console.log session.get('email') #=> Will print the email in the console

While this works, it’s more of a hack and I wasn’t quite happy with it.

Using dependency injection

So I decided to read a bit through ember-data’s code, to see how they manage a few internal things.
And that was a happy decision since what I needed was just there, in just a bit more than 100 lines of code.

So I’ve now divided my session manager into two files. The first one is quite similar to what I used to have.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
App.SessionStore = Ember.Object.extend
  id:    Ember.computed.alias('content.id')
  token: Ember.computed.alias('content.token')
  email: Ember.computed.alias('content.email')

  signed_in: Ember.computed.notEmpty('token')

  password: null

  user: (->
    @store.find 'user', @get('id')
  ).property('id')

  content: (->
    json = App.localStorage.getItem('session')
    data = JSON.parse(json) if json?

    if data?.id? && data?.token?
      data
    else
      {id: null, token: null, email: null}
  ).property()

And the second one is an initializer, which injects my session store into the application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Ember.onLoad 'Ember.Application', (Application) ->
  Application.initializer
    name: 'app.session'

    initialize: (container, application) ->
      application.register 'session:main', application.SessionStore || SessionStore
      container.lookup 'session:main'

  Application.initializer
    name: "app.injectSession"
    before: "app.session"

    initialize: (container, application) ->
      application.inject 'controller',   'session', 'session:main'
      application.inject 'route',        'session', 'session:main'

      application.inject 'session', 'store', 'store:main'

Now, in all my routes and my controllers, I can do the following:

@get('session')

Which will give me my session instance. No more dirty hack to keep the same instance through the app’s lifecycle.

Two things are to be noted in this last portion of code.

  • application.register, which registers our session into the dependency injecter.
  • application.inject, which does the actual injection of the session into the controllers and routes.

The user method

You might have noted that we added a user method in the SessionStore object.
Since we’re using the dependency injecter, we can actually use it to inject dependencies into our session too.

Which is what we do in our initializer.

application.inject 'session', 'store', 'store:main'

This line injects ember-data’s store into our session, making it possible to retrieve instances of models straight from our object.