How can we get the original event in ember's action - ember.js

I'm updating a personal project where I used the ember.js version 0.9.x.
So a new version was released and I have a problem related with ember action.
I have the following html code:
<li><a href="#" id="startApp" {{action activateView target="view"}}> Home</a> <span class="divider">|</span></li>
where, when I click its call this function activateView:
activateView: function(event, context) {
console.log(event);
}
but the event and the context are undefined. I've already tried this.context and it returns undefined.
The main idea its obtain the id of the link when the user click.
I know about routes and the handlebar helper link to, but I really need that id for other things,

In Ember 2...
Inside your action you always have access to the Javascript event object which has the DOM element e.g.
actions: {
myAction() {
console.log(event.target) // prints the DOM node reference
}
}

The event is not passed using the action helper. If you really want the event object, you need to define a view and use the click event:
App.MyLink = Em.View.extend({
click: function(e) {
}
});
and then:
<li>{{view App.MyLink}}</li>
but requiring access to the dom event is a rare case, because you can pass arguments to {{action}}. In your case:
<li><a href="#" id="startApp" {{action activateView "startApp" target="view"}}> Home</a> <span class="divider">|</span></li>
and in the event:
activateView: function(id) {
console.log(id);
}

There are two ways you can receive event object in actions,
1.If you are using component, then you can define any of this list of event names in component and that is designed to receive native event object. eg., {{my-button model=model}}
export default Ember.Component.extend({
click(event){
//oncliking on this componen will trigger this function
return true; //to bubble this event up
}
})
2.If you are using html tag like button then you need to assign a (closure) action to an inline event handler.
{{#each item as |model|}}
<button onclick={{action 'toggle' model}}>{{model.title}}</button>
{{/each}}
In actions hash toggle function will always receive native browser event object as the last argument.
actions:{
toggle(model,event){
}
}
In the below format, action toggle will not receive event object,
<button {{action 'toggle'}}>{{model.title}}</button>
Input helpers such as {{input key-press="toggle" and {{text-area key-press="toggle"
Explained really well in ember guide https://guides.emberjs.com/v2.12.0/components/handling-events/#toc_sending-actions

you need to pass the id into your function like so to have it accessible in the view, you can pass along what ever you want, but in your example this should do it
html
<li><a href="#" id="startApp" {{action activateView "startApp" target="view"}}> Home</a> <span class="divider">|</span></li>
then you have access to the id or what ever you passed in, in the view
js
...
activateView: function(data){
console.log(data); // should be the ID "startApp"
}
...

Just use event handler directly.
Reference: https://github.com/emberjs/ember.js/issues/1684

I don't have enough reputation for a comment, but here is the relevant documentation using Ember Octane.
The callback function will receive the event as its first argument:
import Component from '#glimmer/component';
import { action } from '#ember/object';
export default class ExampleComponent extends Component {
#action
handleClick(event) {
event.preventDefault();
}
}

Related

Ember, No action handler when passing action from template to component

I'm trying to pass an action from a route to a template and then a component.
app/routes/application.js
actions: {
showModal(context) {
console.log("This needs to be triggered" + context)
},
}
app/templates/application.hbs
{{some-component
showModal=showModal
}}
app/components/some-component/template.hbs
<button {{action "showModal" "The context for the action"}}>Press Me</button>
When running this. I get an error saying
"had no action handler for: showModal"
Although, when I include the action inside templates/application.hbs without passing it to a component everything works fine. It's just when passing the action to a component.
app/templates/application.hbs
<button {{action "showModal" "The context for the action"}}>Press Me</button>
This works. I want to call this action in a component though. How can I pass this action to the component?
Firing route actions is a little bit different than firing actions coming from a controller in this context. When you pass in an action to a component from a controller or another component you wrap it in the action helper like so:
{{some-component showModal=(action "showModal")}}
Since the action you're attempting to pass in lives in a route, you need to have to utilize the send method from the controller to call the action in the route. You pass it into the component like so:
{{some-component showModal=(action send "showModal")}}
Here's a twiddle that helps piece it all together.

Ember component call an action in a route or controller

I have a component the main purpose of which is to display a row of items.
Every row has a delete button to make it possible to delete a row. How is possible to pass an action from a template to the component which will trigger an action in a router ?
Here is the template using the component:
#templates/holiday-hours.hbs
{{#each model as |holidayHour|}}
{{holiday-hour holiday=holidayHour shouldDisplayDeleteIcon=true}}
{{/each}}
Here is the component template:
# templates/components/holiday-hour.hbs
...
div class="col-sm-1">
{{#if shouldDisplayDeleteIcon}}
<button type="button" class="btn btn-danger btn-sm mt-1" {{action 'deleteHoliday' holiday}}>
<span class="oi oi-trash"></span>
</button>
{{/if}}
</div>
I'm using the same component to display a row and to create a new item (holiday-hour).
I'm using ember 3.1.2
Thank you
You have to send the actions up from the component to the route. The main way to do this is by adding actions to your component that "send" the action to the parent. Once the action is sent you have to tell the component what action on the route to trigger by passing in the action as a parameter. Below is an example of how to do this.
Component js
# components/holiday-hour.js
...
actions: {
deleteHoliday(){
this.sendAction('deleteHoliday');
}
}
Template for route
#templates/holiday-hours.hbs
...
{{#each model as |holidayHour|}}
{{holiday-hour holiday=holidayHour shouldDisplayDeleteIcon=true deleteHoliday='deleteHoliday'}}
{{/each}}
Route js
#routes/holiday-hours.js
...
actions: {
deleteHoliday(){
//code to delete holiday
}
}
I will try to give a general answer because your question is not giving enough/all info regarding the route actions etc. Long answer short, using closure functions. Assuming this is your route js file routes/holiday-hours.js
import Route from '#ember/routing/route';
export default Route.extend({
model(){ /*... some code */ },
setupController(controller){
this._super(controller);
controller.set('actions', {
passToComponent: function(param) { //.... function logic }
})
}
});
Note: in the above snippet, I'm using setupController to create actions. Alternatively, you can put the actions inside a controller file otherwise actions directly inside the route will throw an error.
So I want the action passToComponent to be called from the component. This is what you do to make it accessible inside the component.
{{#each model as |holidayHour|}} {{holiday-hour holiday=holidayHour shouldDisplayDeleteIcon=true callAction=(action 'passToComponent')} {{/each}}
Now we have passed the action to the component and here's how to call it from the component. Note: I have added a param just to show that it can take a param when called within the component.
import Component from '#ember/component';
export default Component.extend({
actions: {
deleteHoliday: ()=> {
this.get('callAction')() /*Pass in any params in the brackets*/
}
}
});
You will also see demonstrations using sendAction which is rather old and acts more of an event bus that is not very efficient. Read more from this article

Now that Ember.View is deprecated, how does one access event objects for events triggered in the top-level application.hbs template?

I am upgrading an application that customizes Ember.View for the top-level application.hbs. There I have an event handler that needs access to the event object that gets passed in:
ApplicationView = Ember.View.extend({
click(event) {
// Need event here.
}
});
Now that Ember.View is deprecated, I'm not sure how to replace this logic.
I could add an action handler at some div that would capture the event of interest in application.hbs:
<div {{action "topLevelClick"}}>
...
</div>
But although this fires, I don't have access to the event object.
Any thoughts on how to handle this?
Actions declared as DOM event handlers do pass the event:
{{!-- application/template.hbs --}}
<div onclick={{action 'topLevelClick'}}>Click Me</div>
// application/controller.js
actions: {
topLevelClick(event) {
console.log('topLevelClick', event);
}
}
This works on Ember 1.13.13; I haven't tried 1.13.11, though it also supports these kinds of event handlers in general.
By default, the action handler receives the first parameter of the event listener, the event object the browser passes to the handler.
Therefore, in your action you can get the event as the first parameter.
Let say in your controller for application, you have action as:
actions: {
topLevelClick: function(event){
console.log(event);
}
}
this will print on the console, the actual browser event.
Hope this helps.

ember.js | How to bind an event of a sub-component to an action of an outer component

unfortunately i am not able to figure out, how to receive an event of a component i use from within a component.
What i mean actually sounds harder than it is, consider the following toy example, with a component my-outer and another component my-inner (a short explanation follows the code, at the end i link to jsbin).
The templates:
<script type='text/x-handlebars' id='components/my-outer'>
<div {{bind-attr class="isRed:red"}}>Buttons should toggle my background color</div>
<button {{action "toggleRed"}}>It works from my-outer</button>
{{my-inner action="toggleRed"}}
</script>
<script type='text/x-handlebars' id='components/my-inner'>
<button {{action "action"}}>It doesn't work from my-inner</button>
</script>
The javascript:
App.MyOuterComponent = Ember.Component.extend({
isRed: false,
actions: {
toggleRed: function() {
this.toggleProperty("isRed");
}
}
});
my-outer contains a short text, with a background-color, which can be toggled from and to red by invoking the toggleRed action. the first button demonstrates that this works in principle.
now i would like to bind the default action of the second component to this same toggleRed action, that's the point of the following line.
{{my-inner action="toggleRed"}}
But on clicking the second button (which is part of my-inner) an error is thrown and the action is not fired.
How do I fix this example?
http://emberjs.jsbin.com/cabasuru/2/edit?html,js,console,output
Thanks so much in advance
(and this is my first question on so, i am happy about any meta-critics)
Since Components work just like views, easiest way is to get the parentView and forward the action. You may have to handle the action in my-inner like following.
App.MyInnerComponent = Ember.Component.extend({
isRed: false,
actions: {
toggleRed: function() {
this.get('parentView').send('toggleRed');
}
}
});
You can see outer component can be accessed as parentView in inner component. Here is the working jsbin link
http://emberjs.jsbin.com/cabasuru/5/edit
My question actually missed the main point. What goes wrong in the example above, is that the action helper in the inner component
<button {{action "action"}}>It doesn't work from my-inner</button>
does not trigger the default action associated with the component. Instead it invokes a new event named action, which is not allowed to bubble (due to the component confinement).
It turns out, there are two ways to solve that:
Properly reroute the event in an actions block on the my-inner component
<button {{action "my-action"}}>...</button>
together with a definition of the my-action action for my-inner:
App.MyInnerComponent = Ember.Component.extend({
actions: {
myaction: function(){
this.sendAction();
}
}
});
This is basically, the idea #CodeJack proposes, with the difference,
that here we rely on the wiring, which is set-up in the template of my-outer.
http://emberjs.jsbin.com/cabasuru/3/edit
As #torazaburo hinted at, setting the target property on the my-inner component to the my-outer component allows the event triggered from the action helper to bypass the component isolation.
{{my-inner target=controller}} in the my-outer template and a <button {{action "toggleRed"}}>...</button> in the my-inner template.

toastr and ember.js

Is the popup library toastr not going to work with Ember because of direct dom manipulation that ember doesn't like?
Are there any other libraries like this one that work nicely with ember?
Edit
Even through the working example posted below I could not get this to work locally. I finally used Pine Notify which worked straight away.
This works fine in Ember, you just have to handle the event in the right place. The "right place" depends on your implementation. If you want this to be fired from a button within your view, you'll need to use the {{action}} helper passing the action name. Example:
<script type="text/x-handlebars" >
<button class="btn btn-info" {{action showInfo}}>Info</button>
</script>
In the template above, I'm saying that the button should fire the showInfo event, so the Controller responsible for this view should have a function with the same name:
App.ApplicationController = Em.ArrayController.extend({
showInfo: function() {
toastr.info('This is some sample information');
}
});
You can also have the view handle the event; the code below defines a click event, so if you click anywhere in the view, it would run your function:
App.OtherView = Em.View.extend({
click: function(e) {
toastr.error('This is some sample error');
}
});
and in your Handlebars template, you don't have do tell the action since you are already saying in the view class that you want to handle the click event for that view, so you can simple render the view and style it:
{{#view App.OtherView class="btn btn-danger"}}
Error
{{/view}}
Here's a sample in JSFiddle: http://jsfiddle.net/schawaska/YZwDh/
I recommend that you read the Ember Guide about the {{action}} helper