instanceInitializers and store


#1

Hi guys,

I tried to update to latest Ember, everything went great except one issue with initializers. To this time I used this initializer to inject logged in user to all controllers, views and models:

App.initializer({
    name: 'currentUser',

    initialize: function(container, application) {
        var store = container.lookup('store:application'),
            session = $.cookie('session');

        if(!Ember.isEmpty(session)) {
            application.deferReadiness();

            session = session.split(':');

            $.ajaxSetup({
                headers: {
                    'Authorization': session[1]
                }
            });

            store.find('user', session[0]).then(function(user) {
                user.get('profile').then(function() {
                    application.register('user:current', user, {
                        instantiate: false,
                        singleton: true
                    });

                    application.inject('controller', 'currentUser', 'user:current');
                    application.inject('view', 'currentUser', 'user:current');
                    application.inject('model', 'currentUser', 'user:current');

                    application.advanceReadiness();
                }, function() {
                    App.deauthenticate();
                    application.advanceReadiness();
                });
            }, function() {
                App.deauthenticate();
                application.advanceReadiness();
            });
        }
    }
});

However now it throws deprecation:

DEPRECATION: `lookup` was called on a Registry. The `initializer` API no longer receives a container, and you should use an `instanceInitializer` to look up objects from the container. See http://emberjs.com/guides/deprecations#toc_deprecate-access-to-instances-in-initializers for more details.
        at Object.Registry.lookup (http://localhost:9000/bower_components/ember/ember.debug.js:1619:26)
        at Object.App.initializer.initialize [as initialize] (http://localhost:9000/scripts/combined-scripts.js:117:31)
        at http://localhost:9000/bower_components/ember/ember.debug.js:4103:23
        at http://localhost:9000/bower_components/ember/ember.debug.js:4126:9
        at visit (http://localhost:9000/bower_components/ember/ember.debug.js:2147:7)
        at DAG.topsort (http://localhost:9000/bower_components/ember/ember.debug.js:2259:11)
        at Namespace.default.extend._runInitializer (http://localhost:9000/bower_components/ember/ember.debug.js:4125:13)
        at Namespace.default.extend.runInitializers (http://localhost:9000/bower_components/ember/ember.debug.js:4099:12)
        at Namespace.default.extend.boot (http://localhost:9000/bower_components/ember/ember.debug.js:4017:12)

I unsuccessfully tried to refactor this initializer, then searched the web for solution, found discussion on GitHub that lead to conclusion that logic like that may be moved to beforeModel of application route. However it’s not applicable in this case because I definitely need currentUser to be injected everywhere before the application load. Is there any way to workaround this issue, maybe some way of accessing store without raising a deprecation warning?


#2

I would do this by injecting a service that gives access to the current user, rather than the current user object itself. The service could be responsible for performing the authentication also, or you could do that in the instance initializer and set the user on the service.


#3

Are there any examples on how to achieve this?


#4

I have a simular problem trying to give a service access to the store. Hell I don’t even know how to rewrite this to conform to the new rules for initializers. Documentation is shit as ever.

// initializers/nutrition-manager-service.js

export default {
  
  name: 'nutrition-manager-service',
  after: 'store',
  
  initialize: function(container, application) {
    // Give the nutrition manager service access to the data store.
    container.injection('service:nutrition-manager', 'store', 'store:main');
    
    // Make the nutrition manager service available in all controllers and components.
    application.inject('controller', 'nutritionManager', 'service:nutrition-manager');
    application.inject('component', 'nutritionManager', 'service:nutrition-manager');
  }
};

#5

After some research, it appears Ember CLI is not yet ready to work with instance initializers. So for now, just get used to the deprecation messages.


#6

Actually instance-intializers are supported, but they just don’t have a blueprint yet because they have special requirements for testing and need a test helper. I believe most of the work has been done for it, and it should hopefully be merged in soon.

For the original poster’s example it would look like so:

<-- example removed -->

I wouldn’t reccomend using the above setup, as pausing your app to load your user isn’t a great experience usually. I’d instead find a way to put the user payload into your page, then using store.pushPayload and store.peekRecord, push your user record into the store manually, and retrieve it. You can always fall back to deferReadiness if for some reason your payload isn’t present.

Either way, this is the way you’d do it in ember-cli today.

Edit: so my example was incorrect, and has since been removed. It is not possible to pause the app inside an instance initializer: https://github.com/emberjs/ember.js/issues/11247 it is recommended to set this up in the beforeModel hook instead.


#7

thanks trabus! This pointed me in the right direction to access the store from an initializer within my ember-cli app. For future readers: If you need to do something like this in Ember 2+ checkout the documentation here:


#8

I found a very relevant article discussing some of the pain of migrating from a setup where you depended upon deferReadiness: http://miguelcamba.com/blog/2015/06/18/how-to-inject-the-current-user-using-ember-simple-auth/