-
-
Notifications
You must be signed in to change notification settings - Fork 407
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
Add improved actions RFC #50
Conversation
would be written using a closure action as: | ||
|
||
```hbs | ||
<button {{action (action "submit") on="click"}}>Save</button> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is pretty confusing, why double wrapping in action
? What does the closure wrapped action get attached to?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm. It is not double wrapped- the return value of (action "submit")
is a function, which the element-space {{action
helper calls.
String-based actions bubble like before. Function actions are closures to the scope they were created in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<button {{action 'submit'}}>Save</button>
would invoke the localsubmit
action as the button elements on click handler.<button {{action (action "submit")}}>Save</button>
would wrap the local submit action in a function, then attach that function to the button elements on-click handler.
So, yes, technically it would work, but why would you ever do this? If you are calling an action from your own local scope (which would have to be true for (action "submit")
to work), why would you then prefix with {{action
again?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed, will update in RFC to clarify the steps happening. Robert is correct that these cases would have identical behavior, that was what I intended to show.
|
||
```hbs | ||
{{! app/components/my-form/template.hbs }} | ||
{{yield save (action 'reset')}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be attrs.save
?
I am hugely 👍 on this RFC, when can I have it? |
What about {{user-inline-edit submit=(action "saveUser" userModel target="bulkSaveManger")}} |
@workmanw interesting. It is technically possible. This does raise the issue of |
@mixonic I'm not sure how others feel, but for our app this would be greatly beneficial. One of our existing pain points with the current action system is the need for a single component to have different targets for different actions. Wrapping up the Thanks for this RFC! Super excited for this. |
👍 Thank you. |
c525e0b
to
0e744e4
Compare
|
||
```hbs | ||
{{! app/components/my-form/template.hbs }} | ||
{{my-button on-click=attrs.submit}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would {{my-button on-click=(action 'submit')}}
work here as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{{my-button on-click=(action 'submit')}}
would target the submit action on MyForm. The current scope is always what is looked to for an (action
string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only if you wanted to provide my-button
with the "submit"
action from my-form
. As it is written here, my-button
is getting the wrapped action directly from index
controller/route, and you do not need to handle the action and this.sendAction
again (the current manually bubbling).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, right, I have misunderstood the use case here, thanks for explanation!
closed over functions that can be passed between components and passed | ||
the action handlers. | ||
|
||
See [this example JSBin from @rwjblue](http://emberjs.jsbin.com/rwjblue/223/edit?html,js,output) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated the JSBin to work (using 1.11.3): http://emberjs.jsbin.com/rwjblue/466/edit?html,js,output
The original doesn't work (doesn't use template compiler).
Updated with a description of Seems good to merge. |
:👍: |
👍 What would yielding multiple actions look like? Current example: {{! app/components/my-form/template.hbs }}
{{yield attrs.save (action 'reset')}} Multiple yield?: {{! app/components/my-form/template.hbs }}
{{yield attrs.save (action 'reset') (action 'update')}} or {{! app/components/my-form/template.hbs }}
{{yield attrs.save (action 'reset' 'update')}} |
@al3x-edge like: {{yield attrs.save (action 'reset') (action 'update')}} Your third example would curry the argument |
@mixonic Ah. Makes sense. Didn't read well enough apparently -- Thanks Matt |
Does this address all the problems expressed in this RFC? #42 If yes, yay, anything that fixes the most annoying part of ember - having to repeat, redeclare and resend actions every tier is very welcome. If not, meh, because there's a risk here of actions becoming even more confusing. This syntax is bad: {{action (action ?? Overall if actions were based on javascript events and bubbled naturally like them, a lot of these complications could be avoided. |
@mgenev it mitigates the challenge you pointed out in the Summary, although how it fixes that problem is very different than the solutions suggested in that discussion. What used to be: {{! app/index/template.hbs }}
{{my-component openModal="openModal"}} {{! app/components/my-component/template.hbs }}
<button {{action 'popSomething'}}> /// app/components/my-component/component.js
import Ember from "ember";
export default Ember.Component.extend({
actions: {
popSomething() {
this.sendAction('openModal');
}
}
}); Is now simply: {{! app/index/template.hbs }}
{{my-component openModal=(action "openModal")}} {{! app/components/my-component/template.hbs }}
<button {{action openModal}}> However I'm not sure this addresses your desire to have "openModal" be an action on the route reachable from any arbitrary point in the component hierarchy. Closure actions read the function off For the specific case you laid out, I would strongly suggest using a service: {{! app/components/my-component/template.hbs }}
<button {{action (action 'open' target=modalService)}}> /// app/components/my-component/component.js
import Ember from "ember";
export default Ember.Component.extend({
modalService: Ember.inject.service('modal')
}); /// app/services/modal.js
import Ember from "ember";
export default Ember.Service.extend(Ember.Evented, {
actions: {
open() {
this.trigger('didOpen');
}
}
}); /// app/components/my-modal/component.js
import Ember from "ember";
export default Ember.Component.extend({
modalService: Ember.inject.service('modal'),
init() {
this._super.apply(this, arguments);
this._onOpen = () => {
// do something to display popup
};
this.get('modalService').on('didOpen', this_onOpen);
},
destroy() {
this.get('modalService').off('didOpen', this._onOpen);
this._super.apply(this, arguments);
}
}); In this example, the It is off topic for this RFC, but I believe there are at least two follow-up RFCs that would help make this pattern easier:
Still improvements to go, but I'm feeling much better about the patterns we can create with these new actions and with the direction our conversations are going. |
@mixonic thank you for the great detailed explanation and examples. Being able to omit the redundant sending of the intermediate action is indeed a big improvement. What about other instances though in which I'm simply nesting components 4-5-6 levels deep in a route, which I do find myself doing now in the "everything's a component" architecture. Am I to create a service for every route then? This strikes me as a hassle. I'd rather have some kind of global proxy service or a pub-sub pattern which allows me to take actions to the top in every case... Perhaps the future RFC's you mention would make this pattern easier indeed, I'd be happy to see future thoughts there. |
@mgenev I find that most cases where I need access to the "top" level scope (by that we usually mean the controller today, in the future the routable component) can be handled by using {{! app/index/template.hbs }}
{{my-component}} {{! app/components/my-component/template.hbs }}
{{my-other-component passedVal=fromComponent}} {{! app/components/my-component/template.hbs }}
<button {{action omgHowDoIGetToController}}></button> Composing these component with yield keeps access to the parent scope easier: {{! app/index/template.hbs }}
{{#my-component as |val|}}
{{#my-other-component passedVal=val}}
<button {{action omgHowDoIGetToController}}></button>
{{/my-other-component}}
{{/my-component}} {{! app/components/my-component/template.hbs }}
{{yield fromComponent}} {{! app/components/my-component/template.hbs }}
{{yield}} But I definitely acknowledge this isn't always viable. I think the |
@mixonic thanks, I'll explore the pattern with the block component and yielding, @runspired was also telling me that as a solution in the slack. For reference, here's how Aurelia solves these issues very simply: |
I have tried to implement modal dialogue using service pattern as you mentioned in your post, but i am not able to achieve the same ,it would be great help if you please share the working code in JSBIN Thanks |
View as HTML