Best Practices: accessing app config from addon code

I tried using this today on Ember 1.11.3 with a mixin that I was adding to a route, but got the following error:

Failed to create an instance of 'config:environment'. Most likely an improperly defined class or an invalid module export

In my-fancy-project/addon/mixins/foo.js:

import Ember from 'ember';

export default Ember.Mixin.create({
  beforeModel: function() {
    let config = this.container.lookup('config:environment');
    // ...
  }
});

I also tried calling this.container.lookup('config:environment') straight from my application’s route, but also got the same error. Any ideas for what might be causing this?

let config = this.container.lookup('config:environment', {
  instantiate: false
});

Does this work? I haven’t tried this but this error is something I’ve seen while trying to call container.lookup on a non-Ember.Object.

That did not work, but I switched to using container.lookupFactory and it worked.

let config = this.container.lookupFactory('config:environment');

Not sure why the config object is in container.factoryCache instead of container.cache

Interesting, perhaps it’s worth opening a bug within ember-cli.

Is this possible with helpers? I’m attempting to build an addon that only outputs DOM within configured environments, but haven’t been able to figure out a) how to inject the environment within the addon code or b) how to test it.

It goes without saying that this is absolutely trivial when done within the context of an actual application. I’m just having trouble extracting that behavior to an addon.

@steven_ferguson the reason it requires container.lookupFactory is because the config is just a simple POJO singleton, but isn’t registered as such (i.e. it doesn’t register with the instantiate: false option).

When you call container.lookup('foo:bar'), it assumes that the foo/bar module will contain a “class” and it will try to instantiate that class. When that fails (in this case, because ‘config/environment’ turns out to be a POJO, not an Ember.Object), it fails with the error message you saw.

It looks the config is pulled via a simple resolver lookup by filepath - nothing actually explicitly registers the config AFAIK. This could be considered a bug, perhaps, but I know there are rumblings of a significant overhaul of the CLI’s approach to config coming down the pipe.

@rwjblue could you comment on that? Is it worth submitting a PR to somehow register the config as a singleton, or should we just hold off for the rumored config overhaul?

2 Likes

Not sure about testing, but is the this.container.lookupFactory('config:environment') approach not working for you?

I was able to get it working within the addon/ code with the lookupFactory approach. I’m currently exploring how best to test it.

I was able to get this working in my service by doing the suggested this.container.lookupFactory('config:environment'). However, I have to set it as a property either in a method or in a computed property. I dont know of a way to import config from 'configLocation'

I made a little addon for this. Let’s you:

import config from 'ember-get-config';
2 Likes

Now that lookupFactory is no longer available how can I lookup ‘config:environment’ in an instance initializer written in an addon? I’m running into the same error as @steven_ferguson

Failed to create an instance of 'config:environment'. Most likely an improperly defined class or an invalid module export

My code:

function initialize(appInstance) {
    var environment = appInstance.lookup('config:environment').get('environment');

The ember-get-config addon seems to make the config available by looking through require.js’s namespace, something I’m not comfortable with. But @davewasmer’s point on registering it as a singleton by the CLI sounds like something that’s worth considering.

@rwjblue @stefan any pointers?

1 Like

I had to write the indeed nasty stuff like so:

  // reliable way to get the config?
  var configName = Object.keys(window.requirejs.entries).filter((entry) => {
    return entry.match(/\/config\/environment/);
  })[0];
  var config = window.requirejs(configName).default;

Was going to reply here, but decided it might make more sense to make a proposal:

I have been struggling with this issue today, and I finally found this solution:

Ember.getOwner(this).resolveRegistration('config:environment');

See Ember - 4.6 - Ember API Documentation

This deprecation now prevents us from using lookupFactory, and I couldn’t get @nullnullnull’s addon to work for some reason. Anyway, hope it helps someone some day.

6 Likes

Maybe the best solution since the application config is basically a singleton is to create a getter and setter around looking it up:

addon/configuration.js:

let configuration = {};

export function setConfiguration(settings) {
  configuration = settings
}

export default getConfiguration() {
  return configuration;
}

Then create an initializer to set the configuration on startup: app/initializers/initialize-fake-addon.js:

import { setConfiguration } from 'fake-addon/configuration';
import config from '../config/environment';
  
  
var initializer = {            
    name: 'fake-addon',  
    initialize: function(application) {
      if (arguments[1]) { // Ember < 2.1 
          application = arguments[1];     
      }
  
      configure(config.fakeAddon || {});
    },
};
    
export default initializer; 

Then to get the configuration in the addon code:

import getConfig from 'fake-addon/configuration';
...
const config = getConfig();

I’ve been using Ember.getOwner(this).resolveRegistration in my addons for a while and it works great.

let config = Ember.getOwner(this).resolveRegistration('config:environment');

this is generally referred to a component or a route or a service.

1 Like

I’ve been using getOwner now too for a while.

But recently I ran into a project that has travis scenarios that include ember versions < 2.3, where getOwner is not available. Is there a way to include a polyfill or shim for those versions to fix this?

I did see GitHub - ember-polyfills/ember-getowner-polyfill but that relies on you actually using an import, while I would like to only include something for the < 2.3 versions

appInstance.resolveRegistration(‘config:environment’); worked great for me

1 Like

Thanks @ykaragol it worked like a charm

In modern Ember you don’t use Ember global anymore. There is a module import for getOwner instead:

import { getOwner } from '@ember/application';

getOwner(this).resolveRegistration('config:environment');
2 Likes