Sharing actions and methods between subroutes in Ember

I have split my product information into a subroute for each tab as follows:

/product/id (parent route)
/product/id/summary (subroute)
/product/id/details (subroute)
/product/id/log (subroute)
etc

I would like to share some actions and methods between the various subroutes.

For example I currently have the following code that I would have to copy and paste from the summary to details and log controllers.

export default Controller.extend({
  saveCall() {
    // make REST service calls blah de blah
  },
  actions: {
    save() {
      this.set('model.isLoading', true);
      return this.saveCall()
        .finally(() => {
          this.set('model.isLoading', false);
        });
    }
  }
});

I have tried using this.send('save') to ā€œbubble upā€ the action to the parent controller but I get the message:

An action named ā€˜saveā€™ was not found in <ui@controller:product/details::ember436>", ā€¦

Normally with components I would pass a closure action to the component but as these are controllers on subroutes I canā€™t see how I can do that.

I have the following ideas for how to get around this:

  1. Create a shared base class and extend all the subroute controllers from this base class.
  2. Inject the parent controller into the subroute controllers.
  3. Create a mixin for the shared code and add that into each subroute controller.
  4. Create a service(s) and call this from the actions of each subroute controller. This would still require a set of actions on each subroute controller but each one should hopefully just be a one-liner.

Which of these 4 options or any other is the canonical more ā€œEmberyā€ way to do this please?

Do each of the subrouteā€™s require their own controller? If they do not, you can set the controllerName property on each subroute to the parent controller. Then each subrouteā€™s controller context would use the one specified. In this case, product. If you need each subroute to use itā€™s own controller, injecting the parent one is a good approach. You could then invoke this.productController.send('save'). As an aside the {{action}} lets you also target where to find that action via target=someContext.

I think the list you present would each work. Services give you a way to group the behavior more semantically so it is not spread across the application in different places. So Iā€™d vote for configuring the subroutes to use the parent controller or option 2 or 4.

Do each of the subrouteā€™s require their own controller? If they do not, you can set the controllerNameproperty on each subroute to the parent controller. Then each subrouteā€™s controller context would use the one specified.

I didnā€™t realise you could do that. That is awesome, thank you. It works perfectly! Turns out option 5 was the answer. So glad I asked here first.

1 Like