Add component to App from addon in development mode

Hello, I build an addon which creates a user-interface for mirage scenarios which lets you switch between scenarios in development mode. Currently you need to add a component to your application code (into the layout) to make the user interface available which I would like to get rid off.

The goal is that the addon can be configured via the application config to embed the component into the application. I am thinking along the lines of using an instance initializer from the addon which then renders the component and appends it to the body. I kind of think this is an impossible thing todo as the component does not run standalone if that makes any sense, so it has to be part of the application lifecycle. So yeah I am not sure.

Trying to magically insert a component without making that component appear in any of the app’s templates is pretty fragile and error prone.

My own preference would be to have an explicit component that you tell people to add (or add via a generator when they install your addon). It can become an empty component in non-development builds. Or you can compile it out entirely with an AST transform.

1 Like

Yeah thats what I thought, thank you for making this clearer, I think I can live with this. Currently I am excluding the entire addon from prod builds via a funnel exclude which works fine but I need to wrapp the embedded component with a conditional to not render on staging/prod. So when your say empty component - you mean.

  • create a new component which renders the current component
  • then exclude everything but this one component which has the same conditional I mentioned above

Is there a better way?

On the side note of excluding everything but one component, is the following the approach someone would take ?

const tree = this._super.treeForAddon.apply(this, arguments)
if (this.app.env !== 'development') {
  return new Funnel(tree, { 
    exclude: ['**/*'],
    include: [.....], // the one component
  })
}

I am not quite sure if this would work.

UPDATE:

Note, in the case when a file matches both an include and exclude pattern, the exclude pattern wins

That mighty mean that the exclude will overwrite any include I put in the funnel :frowning:

You don’t need to have two components, you can have a single component but switch the implementation using broccoli.

For example, you can have a normal addon directory that contains the real files that implement your component, and a second addon-disabled directory that contains only an empty version of the same component.

init() {
  this._super.apply(this, arguments);
  if (!shouldBeEnabled()) {
    this.treePaths.addon = 'addon-disabled';
  }
}
1 Like

Sorry to ask but where do you add this for it to work. I have been trying the index.js of addon but it is not quite working.

Yes, the addon’s index.js file. Not /addon/index.js, just index.js.

My example was pseudocode, you need your own shouldBeEnabled().

I finally got around the documentation and it makes now a little more sense why this was not working. I added the init to the options and that is definitely wrong. According to the docs -> https://ember-cli.com/api/classes/Addon.html the init does not have the this.app available. So I moved this into included it seems to work as expected.

included() {
    // eslint-disable-next-line prefer-rest-params
    this._super.included.apply(this, arguments)
    const env = this.app.env
    if (env !== 'development' && env !== 'test' ) {
      this.treePaths.addon = 'addon-disabled'
      this.treePaths.app = 'app-disabled'
    }
  },
1 Like