I have an ember view, called picker, which is responsible for detecting click and mouseMove events on a div. Those events, when detected, send the respective calls to the corresponding controller.
export default Ember.View.extend({
templateName: 'picker',
click: function (event) {
this.get('controller').send('addColor', event);
},
mouseMove: function (event) {
this.get('controller').send('updateColor', event);
}
});
My app requires that I perform some logic using the pageX and pageY properties of the event object and also determine some scaling factors using the properties of the div (offset, width, height etc.).
Where should that work be done? Should it all be contained within the view and just passed through to the action methods as parameters or should I pass the event object through and make it a responsibility of the controller? My gut says the former...
This sounds like it should be wrapped in a component.
You can wrap the mouseMove and click event handlers in the component and the controller can pass in the actions to call when those events fire. for example you can have a component like
export default Ember.Component.extend({
click: function (event) {
this.sendAction('click');
},
mouseMove: function (event) {
this.sendAction('mouseMove');
}
});
and the in the template you would use it like
<div>
{{#x-picker click="addColor" mouseMove="updateColor"}}
<span> Track mousemove and click in here </span>
{{/x-picker}}
</div>
Related
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.
sendAction() in an Ember.Component bubbles to controller by default which is expected. But i have 2,3 actions which i rather need to send to corresponding view which is using the component. In templates we set action to view using target=view. Can we do that?.
Update: Currently as a work around I am sending my view object to component which from there calls view.send() to send the action. But i feel this is not correct.
Ok after some thinking I believe i know that you mean. If you have a component and you have a action it will be handled by the component itself. If you want to send a action outside the component you would use sendAction.
Now to target the view of which holds your component since your component is base on a view, you can probably do this.get('parentView') to get the parent view and then chain send('nameOfAction')
So it would be this.get('parentView').send('nameOfAction') from within the component action and it will then trigger the action on the parent view of which the component is embedded.
So in your component you could have:
App.DemoCompComponent = Ember.Component.extend({
actions: {
internalTrigger: function() {
//just an alert to make sure it fired
alert('Internal action was caught on component');
//this should target parent view component is in
this.get('parentView').send('viewTriggerTest');
}
}
});
Now lets say you have you component in the index template you could:
The template would be:
<script type="text/x-handlebars" data-template-name="index">
<h2>Inside Index Template</h2>
{{demo-comp}}
</script>
The Index View Code would be:
App.IndexView = Ember.View.extend({
actions: {
viewTriggerTest: function() {
alert('View Trigger Test Worked on Index!');
}
}
});
Here is a jsbin
http://emberjs.jsbin.com/reyexuko/1/edit
For latest Ember 2.9 the recommended approach is to pass a closure action to the child component. The property target and parentView are private ones.
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();
}
}
I have button views that are part of a set of Ember.js form helpers I wrote. E.g. MyAddressFormView has the following template:
{{#form}}
{{textArea address}}
{{submitButton}}
{{cancelButton}}
{{/form}}
To handle the "Submit" button, I let the "submit" form event bubble, and handle it in the submit method of MyAddressFormView.
But how do I do the same for the "Cancel" button? For instance, is there any way I can trigger a custom "cancel form" event in a child view, let it bubble, and handle it in an ancestor view?
I would suggest triggering a jQuery special event and registering the event with a mapping on your Ember app. For example:
Ember.Application.create({
customEvents: {
// key is the jquery event, value is the name used in views
formcancel: 'formCancel'
}
});
And in your cancelButton view:
click: function(evt){
Ember.$().trigger('formcancel')
}
This will bubble in the same way that other mapped Ember DOM events do.
Ember.ActionHandler bubbles events through its "target" property. It's possible to override Ember.View's target to let events bubble through parentViews by default.
Include this mixin first:
(function() {
Ember.View.reopen({
// Let actions bubble to parentView by default.
target: function() {
return this.get('parentView');
}.property('parentView')
});
})();
Then just send the event like you'd normally do:
tap: function() { this.send('someEvent'); }
I'm finding that jQuery observers aren't bound to elements that are not shown in handlebars logic.
Let's say I have the following;
{{#if person}}
Welcome back, <b>{{person.firstName}} {{person.lastName}}</b>!
{{else}}
Please <a class="login">log in</a>.
{{/if}}
<script>
$('.login').click(function() {
alert("Hi there.");
});
</script>
If I run in console, person = null (or whatever's needed to convince that person is empty) - the login observer doesn't work. I'm already using embers didInsertElement() to load a few other things, but is there a "onChange" event I can hook into so I can rebind event observers?
The big question is why you want that? Ember has excellent built in support for click handlers without going via jQuery. The reason your <script> is not working is likely to be down to the deferred way ember inserts views into the DOM. When you do Ember.View.append() the element is inserted in the DOM later.
That said, here's a fiddle that does what I think you want attaching the jQuery click handler in didInsertElement().
http://jsfiddle.net/algesten/5LPPz/1/
didInsertElement: function () {
// appending click handler directly with jQuery
$('.login').click(function() {
alert("Hi there.");
});
}
However the ember way would be to just use the click implicit handler function:
http://jsfiddle.net/algesten/5LPPz/2/
click: function () {
alert("Hi there.");
}
N.B. the latter handler attaches to the surrounding handlebar div and not the a, but clicks bubble.
The problem your facing is that javascript can only bind to elements that exist in the dom. Once you add a new element it wants you to re-bind those events. Luckily, jQuery is your friend on this one.
<script>
$('body').on('click', '.login', function() {
alert("Hi there.");
});
</script>
Ideally, your selector is the closest parent to .login that doesn't get added by javascript. The above is safe bet if you're not sure