Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

action does not bubble from controller #12581

Closed
bmeurant opened this issue Nov 10, 2015 · 13 comments
Closed

action does not bubble from controller #12581

bmeurant opened this issue Nov 10, 2015 · 13 comments
Assignees

Comments

@bmeurant
Copy link
Contributor

Hello,

First, thanks everyone for all this great work

I was playing with actions through controllers and routes and I ran in some strange behaviour, IMO :

  1. Controller action sometimes required

    Using this template, I can perfectly run my app without 'save' action on my controller :

     <div {{action "save" model.id}}>confirm</div>
     {{input enter="save" value="confirm"}}

    But using this one, any of these components require an explicit 'save' action in controller (error: "An action named 'save' was not found in ...") :

     <div onclick={{action "save" model.id}}>confirm</div>
     {{input enter=(action "save" model.id) value="confirm"}}
  2. Action is not bubble from Controller to Route

    Again, the first template sample if fine and, even if I define a save action in my Controller, the
    action is bubbled to Route if y explicitely return true, as explained in documentation.

     // route
     actions: {
       save: function() {
         console.log('route save');
       }
     }
    
     // controller
     actions: {
       save: function() {
         console.log('controller save');
         return true;
       }
     }

    But using the second template, without any route or controller modification, the action is
    well handled by my controller but does not bubble

Some here are my questions :

  • Why does specifying event type or using Handlebars value context require an action on the controller ? I does not find this really consistent regarding standard behaviour.
  • Is there another way to pass a parameter to an action inside an {{input}} than using subexpression ?
  • Why does the action is not bubbled from controller to route in my last case ?

I did not found any explanation on these points so I guess these are, at least, undocumented behaviour. If I am wrong can you point me the doc, please ?

You can find a complete running example here : http://emberjs.jsbin.com/takuha/edit?html,css,js,console,output

Thx

@OrKoN
Copy link

OrKoN commented Nov 10, 2015

{{action}} helper works a bit differently compared to the normal actions. There are 3 contexts that affect the behavior of the action helper:

Attribute context, and Handlebars value context

In these contexts, the helper is called a "closure action" helper. It's behavior is simple: If passed a function name, read that function off the actions property of the current context. Once that function is read (or if a function was passed), create a closure over that function and any arguments.

http://emberjs.com/api/classes/Ember.Templates.helpers.html#toc_passing-functions-with-the-action-helper

In this context, the action has to be defined.

Element Space

If the context of a template is a controller, actions used this way will bubble to routes when the controller does not implement the specified action. Once an action hits a route, it will bubble through the route hierarchy.

http://emberjs.com/api/classes/Ember.Templates.helpers.html#toc_attaching-actions-to-dom

In this context, it does not need to be defined and it will bubble to the route.

@bmeurant
Copy link
Contributor Author

Ok. Thanks a lot for your response.

I can understand these technical differences even if I am not comfortable with this lack of consistency between the two behaviour. IMO these two cases are not conceptually so different and I admint I have some difficulties to understand such a different behaviour.

But the main point IMO and the most annoying one for me is the way to pass parameters to an action inside an {{input}} helper. I found really, really strange to have to provide an action in my controller to handle an additional parameter to {{input}}.

... Let's imagine that I used {{input enter="save" value="confirm"}} to be able to handle the action on my route. If, for a reason or another, I have to deal with an action param and, then {{input enter=(action "save" model.id) value="confirm"}}, I cannot understand why I have to change my app to add, in my controller, nothing but an explicit call to a sendAction to my route.

Is this even possible ? Why do the action cannot bubble, even with a return true ?

@mixonic
Copy link
Sponsor Member

mixonic commented Nov 10, 2015

@bmeurant this difference is definitely a painful one, and is not intended to be permanent. Work is underway to improve the situation in two RFCs:

Once we land these (perhaps 2.4), the legacy element space actions can be deprecated.

Until then, I suggest:

  • Always use a closure action. For example: {{input enter=(action 'save') value='confirm'}}. Passing the closure action instead of a string will opt you into the new world instead of the bubbling one.
  • If you need to send an action to a route (which is not often, in practice a component or controller can deal with models and controllers can deal with transitioning routes), do that from a controller using sendAction.

Using the above techniques you can avoid bubbling and the inconsistency entirely.

Why do the action cannot bubble, even with a return true ?

Closure actions are simply functions, and their return value is returned to the site of their invocation. This is immensely powerful, and incompatible with true meaning an action should bubble. In general, new actions take advantage of lexical scoping, matching the behavior of plain JavaScript. This transition is a bit bumpy, but the new world will be far more familiar to new developers than memorizing our bubbling rules.

I will leave this open until we have deprecated element-space actions, though it probably won't be closed by any one PR.

@flpms
Copy link

flpms commented Nov 10, 2015

Is this question related with the issue?

http://stackoverflow.com/questions/33634755/nothing-handled-the-action-emberjs-2

@nightire
Copy link
Contributor

@mixonic You sir really make this clear and simple, this answer really should add to the documents!

@bmeurant
Copy link
Contributor Author

@mixonic thx for your answer and links to RFCs I have to read closely now

Moving to a consistent solution is a good thing and closure actions appear to be really powerfull indeed.

That said, not sure to understand for now all consequences. It means that, once element space actions will be deprecated, it will be required to provide a controller / component action handler that will not bubble anymore to route, isn't it ?

I am not totally comfortable with that point. I use to handle many actions in my routes : all actions concerning retrieving the model or sending it, dealing with API errors, redirections, etc. IMO, routes are the best place to handle that beacause it is closely related to routing and you can manage cancellation, rollback (with willTransition for example).

Even if, you are right, model can be reached and transitions can be called from controllers / components (routable ones ?), I don't see these operations as a responsibility of a controller or component. Are they for you ?

Does it means that Ember 2.4 & further will force me to catch an action in a controller and rethrow it if I want to handle it in my routes ? This will bother me a little because I think that on of Ember strength is to not require us to add boilerplate or non value added code.

But maybe am I missing something else ?

@OrKoN
Copy link

OrKoN commented Nov 11, 2015

Does it means that Ember 2.4 & further will force me to catch an action in a controller and rethrow it if I want to handle it in my routes ? This will bother me a little because I think that on of Ember strength is to not require us to add boilerplate or non value added code.

@bmeurant there will be no routes or controllers as we know them soon. Routable components and normal components will replace them.

More info: https://gist.github.com/samselikoff/1d7300ce59d216fdaf97

@mixonic
Copy link
Sponsor Member

mixonic commented Nov 11, 2015

Does it means that Ember 2.4 & further will force me to catch an action in a controller and rethrow it if I want to handle it in my routes?

Well, nothing is forced until 3.0 ;-) Even in 2.4 I doubt there will be a deprecation. We want to move things over slowly and deliberately.

But yes, bubbling will be gone. This is a longer discussion than seems appropriate for this ticket, and has been discussed on the improved actions RFC and elsewhere. The tl;dr is that bubbling, though well understood by those who master it, is still very difficult to use for new developers. Even experienced developers, given a template calling {{action 'foo'}} basically need to guess about where the handler for foo is. The new actions system is designed to be more static, more predictable. Given a (action 'baz'), you know that baz can only possibly be defined on the current component or controller. If you have onclick={{action attrs.nuf}}, you know nuf is a variable and can see where it came from (in this case passed as an attr, in other cases a block param.

We are confident this system is easier to understand and work with than a bubbling solution. In addition we get bonuses like actions that really return a value, and fantastic new currying/partial-application behavior from by using closure patterns.

The "boilerplate" you fear is tremendously minimal. Passing an action is as easy as passing any other variable, and in routable components the route will just provide actions down to the component as attrs.

@tim-evans
Copy link
Contributor

Passing an action is as easy as passing any other variable, and in routable components the route will just provide actions down to the component as attrs.

This sounds wonderful. I prefer to have business logic in routes, and it would be wonderful to be able to have the ability to pass actions through via this mechanism. Is there any firm idea on how passing actions through the routes will work?

Cf. Would I pass function references in the attrs property on routes?

@mixonic
Copy link
Sponsor Member

mixonic commented Nov 11, 2015

@tim-evans yeah. The details of the implementation are obviously the stuff of an RFC, and I don't know exactly what it will look like, but attrs on the routable component is provided by the route and thus attr.someAction would also be provided by the route to the component.

@tim-evans
Copy link
Contributor

Thanks for clarifying! Glad to know that this is gonna be part of routable components 👍

@bmeurant
Copy link
Contributor Author

@mixonic thanks again for answering.

I still have to dig a little into all this stuff, routable components, etc.

Anyway, you're right, this discussion is too long for this ticket. I will take a closer look to previously pointed RFCs.

thanks again

@locks
Copy link
Contributor

locks commented Apr 20, 2016

I am closing this issue as some resolution seems to have been reached.
Feel free to re-open the issue if you think it's still justified, thank you!

@locks locks closed this as completed Apr 20, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants