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