Best Practices: accessing app config from addon code

I’m building an addon that needs to read some configuration from the parent app’s config (the parent app provides some options to configure the addon), but it seems that there is no way to import the parent app’s config in addon/ code. I’m guessing it’s because the build process builds that folder separately and eventually concats the separate builds into vendor.js.

Assuming that’s the case, does anyone know of any simple / elegant way to get app config into addon code, without resorting to something like what ember-simple-auth does (maintaining a global Configuration object and injecting the config into that via an initializer at runtime)? Or is that approach the best practice?

2 Likes

Depending on what you have in your addon folder, you can access the app’s config.

For example if you have a Mixin that is used by some object in the consuming that is looked up via the container/resolver (like route, controller, component, view, etc) then you can lookup the app’s config like the following:

modulePrefix: Ember.computed(function() {
  var applicationConfig = this.container.lookup('config:environment');

  return applicationConfig.modulePrefix;
})
4 Likes

Ah, I see what you mean.

That’s helpful for most of my use cases, but one scenario that comes to mind is a service. It’s just a simple Ember.Object, with no reference to the container. I could inject one via an initializer, but is there a more elegant way in that scenario?

Anything looked up via the container, gets a container. So if your service is either injected into other items (controllers, routes, etc) or looked up via container.lookup('service:foo') it will have a container property set on it.

How is your service integrated into the rest of the application?

1 Like

Just a file in services/my-service that is imported directly by any consumers via:

import myService from '../services/my-service'

Perhaps that’s where I’m deviating from best practice. Are services usually injected via an initializer?

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();