Execute action on link-to - ember.js

I want to add an action to a {{link-to}} helper.
For example, in a modal component, I use {{link-to}} to open a new route. When the user clicks the link, I want to close the modal through an action.
One possible solution is to wrap an action around a {{link-to}}:
<button {{action "close"}}>
{{#link-to 'register'}}Registreer{{/link-to}}
</button>
Is this the best / cleanest solution possible? Or can I add an action to a link-to helper?
Another approach might be to transition and close in a custom action, but this approach is not
usable for use in a component:
// template.hbs
<button {{action "link" "register"}}>Registreer</button>
...
// template-controller.js
actions: {
link: function(routeName) {
this.transitionToRoute(routeName);
this.close()
}
}

You should do teardown operations such as closing modal boxes within the willDestroyElement event of your current view.
By doing this, you could just {{link-to 'register'}} and let your view (template-view.js) manage the fact that there is a modal to close or not.
Checkout the docs of willDestroyElement HERE

Related

Prevent click into {{input}} component from propagating/bubbling up (ember)

I have an {{input}} inside of an element that itself has an action.
How do I prevent clicking into the input from triggering the click event on the parent element?
I have tried bubbles=false.
I have also tried extending {{input}} and in the extended input I caught the click event and called event.preventDefault()
Please try my test case: https://ember-twiddle.com/a2cee9abd63a7400124e2745a7820cf8?numColumns=2&openFiles=controllers.application.js%2Ctemplates.application.hbs
Example hbs:
<div {{action "alert"}}>
I don't want the click into the input to trigger the div's onlick. Help!<br/>
{{input bubbles=false}}
</div>
Define component say my-input which extends Ember.TextField and define click function and return false to prevent bubbling.
import Ember from 'ember';
export default Ember.TextField.extend({
click(){
console.log('click my input');
return false;
}
});
For my-input component, you dont need to define anything to handle click event while including the component. you can just say {{my-input }}
Here is the working twiddle

How to stop the inner action from bubbling to the outer link-to helper?

When clicking the "edit" to trigger the action "edit":
{{#link-to "pages.show" page class="list-group-item"}}
{{page.name}}
<span class="badge" {{action "edit" page preventDefault=false}}>edit</span>
{{/link-to}}
Then the action is triggered (e.g. the edit page is opened), but in the next second the link-to redirect is done so I end up on the "pages.show" route.
Expected: Solely the action "edit" is invoked and the click event(?) is not "bubbled" to the link-to helper.
Side notes Ember 2.2 is used and the template above is part of a component.
PS: I red that using preventDefault=false inside an action would stop this type of behavior - but obviously the link-to helper gets the information from somewhere else.
What you need is called event bubbling. You can disable it by using bubbles=false inside your action helper, so your click event won't bubble up to the link-to element
{{#link-to "pages.show" page class="list-group-item"}}
{{page.name}}
<span class="badge" {{action "edit" page bubbles=false}}>edit</span>
{{/link-to}}
http://emberjs.com/api/classes/Ember.Templates.helpers.html#toc_event-propagation
preventDefault is a different thing, when setting preventDefault to false, you allow the default browser action of the DOM event.

Integrating ember-modal-dialog inside a site header Ember 2.0

I'm very new to the Ember framework and I have a question regarding getting a modal setup. I have the site-header as a component. When I click a login button I'd like for a modal to popup. I found the Ember Modal Dialog
plugin and was able to set it up so that there is a modal always shown in application.hbs. However, I'm having trouble understanding a couple of things but first here are my files.
application.hbs
{{site-header}}
{{outlet}}
{{#if isShowingModal}}
{{#modal-dialog close="toggleModal"
alignment="center"
translucentOverlay=true}}
Oh hai there!
{{/modal-dialog}}
{{/if}}
{{site-footer}}
site-header.js
import Ember from 'ember';
export default Ember.Component.extend({
isShowingModal: false,
actions: {
toggleModal: function() {
this.toggleProperty('isShowingModal');
}
}
});
So, I have this code for the button in my site-header.hbs:
<li class="login-button"><button class="btn btn-block btn-danger" {{action 'toggleModal'}}>Login</button></li>
As I understand it, action is saying to find toggleModal property in the site-header.js and execute the function above which does the property toggling.
However, how does application.hbs "see" the value of isShowingModal? Can it even see that value since the modal isn't showing up?
When most developers have modals, do they all go inside application.hbs since you want them to appear in the middle of the screen {{outlet}} area? How can you improve the process for including multiple modals?
What changes should I make to make the modal show up when the user clicks a button in the header?
Ok give this a try. In site-header.js
export default Ember.Component.extend({
actions: {
toggleModal() {
this.sendAction('toggleModal');
}
}
});
Application.hbs
{{site-header toggleModal='toggleModal'}}
{{outlet}}
{{#if isShowingModal}}
{{#modal-dialog close="toggleModal"
alignment="center"
translucentOverlay=true}}
Oh hai there!
{{/modal-dialog}}
{{/if}}
{{site-footer}}
The application controller
export default Ember.Controller.extend({
actions: {
toggleModal() {
this.toggleProperty('isShowingModal')
}
}
})
So the action is sent from the site-header component to the site-header component. From there it gets sent to the application controller. In application.hbs, toggleProperty='toggleProperty' connects the action from the component to the controller's action hash. In the controller action hash, it gets handled by toggleModal() and toggles the isShowingModal property. When the modal is closed, ember-modal-dialog fires an close action that is handled by the toggleModal() which again toggles the isShowingModal property.
Application template cannot see the properties of site-header. Only site-header component can see its properties. There are other methods to access them.
It is not necessary to include all modals in application. The plugin will most probably handle the positioning. In your case, you can move the modal code to the site-header component also.
Put the modal code in your site-header template. (or) You can have isShowingModal variable in application, send an action from site-header to application and toggle its value.

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