How to dry route actions with component in EmberJS

284 Views Asked by At

(EmberJS 2.14)

I want to have DRY apps with ember, however, it seems it is impossible to do with route actions. When I try to use a component for the button triggering the action it fails :

new.js :

  export default Ember.Route.extend({
      model() {.....},
      actions: {
          saveStuff(newStuff) {
            newStuff.save().then(() => this.transitionTo('stuffs'));
          }

new.hbs

{{basic-button texte="Create" theAction=(action 'saveStuff' model)}}

Error :

Assertion Failed: An action named 'saveStuff' was not found in (generated Stuffs.new controller)

I have the same problem with the delete action and the update action. I can't resign myself to have the same code and template lines in every routes and views that needs it.

3

There are 3 best solutions below

0
Donald Wasserman On

Routes don't handle actions well. There are 3 options:

  1. You can handle your actions in the route using this addonn https://github.com/DockYard/ember-route-action-helper. This defines a helper to use like:

    {{basic-button texte="Create" theAction=(route-action 'saveStuff' model)}}

  2. you could add a controllers/new.js controller and handle action there. (EDIT: If you're concerned about multiple controllers you can add it to the application controller controllers/application.js and that will handle all actions at the the top if you truely have a global action (like "logout")

  3. You could wrap the whole thing in a component and pass the model.

2
real_ate On

We just wanted to add to the answer that Donald has already, and hopefully give you another example of how to do what you are trying to achieve.

As you mentioned "code reuse" I imagine you are already making use of components in your application. When I have wanted to have the same "save" action re-used between multiple routes I have usually implemented a component that dealt with the editing of a model and the actual save process.

For example, my main application shares the same form to create and to edit an instance. In my app/instances/new/template.hbs I am making use of an edit-instance component, and I will then reuse that same component in the app/instances/edit/template.hbs

{{edit-instance instance=model}}

I have created a twiddle here that demonstrates this principle in more detail. One of the other things that it does is it actually encapsulates the creation of new instances inside the component by injecting the store service.

export default Ember.Component.extend({
  store: Ember.inject.service(),
  ...
})

This is a handy little trick that may seem odd at first because all of the documentation examples only show using the store in the route file. It's not always best practice to use the store in a component but when you need to, or if it simplifies your implementation it's a useful tool to have!

I'm not sure I stand by the UX/UI of the example in the Twiddle but I think it helps to explain just what you can achieve once you try to implement code-reuse inside a component.

You can see the entire example working on this Ember Twiddle and you can see us solving this problem live on this YouTube video

I hope this helps

0
Joe Hartzell On

I would suggest just creating a controller. I do like Donald's suggestion of using route-action-helper by DockYard ( it's a great package ). But even it says that you probably don't need it.

You probably don't need to use this addon.

If you are trying to not install any extra packages, I would strongly suggest just moving your action to a controller. Controllers are not deprecated and are going to be around for a while, and were made for these kinds of situations. I would also suggest reading this article by DockYard that they reference if you are not familiar with Controllers.

Article: https://dockyard.com/blog/2017/06/16/ember-best-practices-what-are-controllers-good-for