HOWTO: Access an Action which is defined in another Controller

From @angus post in Mattermost chat a few days ago:

To access an Ember closure action defined on another controller, you can use getOwner

import { getOwner } from 'discourse-common/lib/get-owner';

export default {
  setupComponent(attrs, component) {
    const controller = getOwner(this).lookup('controller:topic');
  },
}

Once you have the controller you can use controller.send('action')

I can confirm this worked great. I reformatted the code a little, as per my example below:

import { getOwner } from 'discourse-common/lib/get-owner';

export default {
  actions: {
    convertPrivateStoryToPublicStory() {
      const controller = getOwner(this).lookup('controller:topic');
      controller.send('convertToPublicTopic')
    },
    convertPublicStoryToPrivateStory() {
      const controller = getOwner(this).lookup('controller:topic');
      controller.send('convertToPrivateMessage')
    },
  },
};

Some brief notes.

  1. You can store the controller in a computed property on setup to DRY up the code.

  2. Be aware that looking up a controller in the container is basically a “fallback” method, to workaround the fact that in the plugin you’re limited to the plugin-specific contexts (e.g. the attributes passed to a plugin outlet). If there is a way to access the same actions or properties without looking up the class this way, it’s always better to do that.

Also, an unrelated note about plugin template connectors. In most instances you should be including a shouldRender callback that uses your plugin_enabled site setting. This will prevent the markup being inserted when your plugin is disabled.

Example:

export default {
  shouldRender(_, ctx) {
    return ctx.siteSettings.plugin_enabled;
  }
}