Ember - actions within link-to blocks - ember.js

I am building a component in Ember.js which includes a table. Each tr in that table is created using {{#link-to ...}} so that clicking anywhere on the row will take a user to another route.
However, I'd like to make a td element in those rows clickable to open a link in a new window. I'm doing this as an action.
Right now, clicking the proper td element will both trigger the {{#link-to}} redirect and activate the action on the td element as well.
Instead, I'd like a click on the proper td element to only trigger that element's action, and ignore the {{#link-to}} event above. How would I go about doing this?
This is what my code looks like:
<table>
{{#link-to 'widget' widget.id tagName='tr'}}
<td>Go to link-to</td>
<td {{action 'sendMail' widget.email}}>Send Email</td>
{{/link-to}}
</table>

Look at this twiddle implemented your case.
You need to call event.stopPropagation for that you need to have event object, to get it I used onclick={{action
<table>
{{#link-to 'my-route' tagName='tr' bubble=false}}
<td>Go to link-to</td>
<td onclick={{action 'sendMail' }}>Send Email</td>
{{/link-to}}
</table>
In sendMail, you need to stop event propagation.
actions:{
sendMail(event){
console.log('sendMail');
event.stopPropagation();
}
}

Related

action inside li tag does not triggered sometimes in Ember js 1.10

I have multiple tabs in my page which link to some internal routes. When an user clicks on a tab ,the route has to be rendered and also the tab has to be highlighted but once in a few clicks the highlighting does not shift to the new tab and the old tab remains highlighted but the route of the clicked tab gets rendered.
The highlighting is done by an action inside the li tag and the #link-to is nested inside the li tag.
In my investigation till now what I have found is that the when this happens the click event is not registered. I get a bunch of mouse events but no click event. Seems like the click event is eaten up.
<ul class="nav nav-tabs">
{{# each tab in tabBars}}
<li {{action 'someAction'}}>{{#link-to tab.link}}{{/link-to}}</li>
{{/each}}
</ul>
The action should be triggered all times when a tab is click and the new tab should be highlighted.
Try to do something like this instead:
{{#link-to tab.link tagName='li'}}click me{{/link-to}}
(and omit the <li> tag).
Then you can check for the active class to highlight the li element.
The {{link-to}} helper renders a link which will get an additional class="active" if the link matches the current route. You can then style this by CSS.
See https://guides.emberjs.com/v1.10.0/templates/links/ and search for "active".
Other options
Since the {{link-to}} will trigger the tab.link route's beforeModel hook, perhaps this is the place you might want to set view-related properties to be "data-downed" to the <li>. I would personally reserve this hook for business logic.
When I absolutely need to fire an action before transitioning (usually, this is when I want to fire something like a tracking event), I use the invokeAction closure action which comes with the ember-link-action addon although it looks like might only support 1.13.13
{{link-to invokeAction=(action "someAction")}}
Best practice
For the action in the <li> element you generally want to avoid setting actions on non-interactive elements since it creates an accessibility concern for users of assistive technology (see no-invalid-interactive).
However, this doesn't mean you should turn the <li> element into an interactive element by doing something link <li role="button"> as this violates more a11y concerns (see no-nested-interactive).
I found a solution which does not involve actions:
<ul class="nav nav-tabs">
{{#each tab in tabs}}
{{#link-to tab.link id=tab.id tagName='li' }}<a>{{tab.title}}</a>{{/link-to}}
{{/each}}
</ul>
link-to masquerading as li and having a dummy anchor inside it.

How to use a component from within a component in Ember

G'day all,
I'm trying to wrap a component with another to provide a simplified editing wrapper.
The component is to conditionally show either a label or a select component that allows the user to pick the right value.
I want to wrap the power-select component, and pass it's values through to the sub-component, so the page template component reference looks like this:
{{cm-select
title="Country ##"
options=countries
selected=selectedCountry
searchField="name"
action="selectCountry"
}}
"countries" is an array of country objects, and selectedCountry is one of those country objects.
The component template has the following:
<td>{{title}}</td>
<td>
{{#if edit}}
{{#power-select
options=options
selected=selected
searchField=searchField
as |object|}}
{{object.name}}
{{/power-select}}
{{else}}
<small>{{modelElement}}</small>
{{/if}}
</td>
Unfortunately the power-select component renders with an empty list of options.
I thought wrapping those parameters in handlebars might do the trick, but it seems that handlebars in handlebars isn't a valid syntax.
Does anyone have any ideas?
Thanks,
Andy
That should work, I created a twiddle for you, demonstrating your use case. You'll see I updated the your cm-select template to this:
{{title}} |
<button {{action 'toggleEdit'}}>Toggle Edit</button>
<br/>
{{#if edit}}
Search for a Item via {{searchField}}
<br/>
{{power-select
options=options
selected=selected
searchField=searchField
onSelect=(action "itemSelected")
}}
{{else}}
{{search-list
options=options
searchField=searchField
onSelect=(action "itemSelected")
}}
{{/if}}
Where you iterated over options for power-select in the cm-select component, I moved that down into the power-select template. It's better to try and encapsulate some functionality there, instead of having everything in cm-select. I wasn't sure what you had in mind with {{modelElement}} so I just demonstrate what it would look like, using two different components in cm-select.

Ember.checkbox nested in action doesn't change value

Template:
{{#each document in documents}}
<div class="col-md-6" {{action "selectDocument" document}}>{{view Ember.Checkbox checked=document.isSelected}} {{document.name}}</div>
{{/each}}
Controller:
App.IndexDocumentsController = Ember.ArrayController.extend({
actions: {
selectDocument: function(document){
document.set('isSelected', !document.get('isSelected'));
}
}
});
When I click on the div, the checkbox toggles 'checked' property. But when I click on the ckeckbox - nothing happens. What can be the reason?
UPDATED
Link to jsbin: http://emberjs.jsbin.com/nuvocumuteto/1/edit?html,css,js,output
The issue is that when you click on the checkbox 2 things happen.
the checkbox toggles the isActive property, then
the selectRow action is ran which again toggles the isActive property
So the isActive property ends up staying in the same state that it was.
In your case I would get rid of the action, wrap the checkbox in a <label> and set the label to display: block.
The template would look like
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each item in model}}
<li {{bind-attr class="item.isActive:active"}}><label>{{input type="checkbox" checked=item.isActive}}{{item.name}}</label></li>
{{/each}}
</ul>
</script>
and the css would look like
label {
display: block;
}
you would then be able to get rid of the selectRow action completely because clicking on the label will trigger the checkbox.
You can see a working bin here: http://emberjs.jsbin.com/nuvocumuteto/3/edit
I would argue that you are not following "The Ember Way" in two different ways here.
First, Ember.Checkbox is an internal Ember class (http://emberjs.com/api/classes/Ember.Checkbox.html). The recommended way to render a checkbox is to use the Handlebars input helpers (http://emberjs.com/guides/templates/input-helpers/#toc_checkboxes). This is just wrapping Ember.Checkbox anyway.
Second, if you want to update the value of isSelected, the "Ember Way" is to use two-way data bindings. Your code uses one-way data-binding when it reads document.isSelected and then tries to manually re-create the the data-binding in the other direction when the user clicks by manually writing a selectDocument action and calling it from an {{action}}.
Instead, simply bind the Ember Handlebars Input Helper directly to your value like this:
{{#each document in documents}}
<div class="col-md-6">{{input type="checkbox" checked=document.isSelected}} {{document.name}}</div>
{{/each}}

Handlebars linkTo renders empty link

I came accross a wierd situation, using Ember and Handlebars. I have a table of records. The user should click to any record and be redirected to the item detail - quite a usual use case.
However, Ember does not renders the link correctly. It works when I wrap only a single word or element by linkTo tag, but it does not work when I wrap whole table row.
{{#each item in controller.content}}
{{#linkTo "detail" item}}
<tr>
this is part of the link, correctly
<td>and this is not</td>
<td>{{item.someInfo}}</td> <!-- this neither -->
</tr>
{{/linkTo}}
{{/each}}
How can I fix this, when I want whole tr to work as a link?
Tables should only have <tr> fields in them, and <tr> fields should only have <td> fields in them. You're trying to add an ember tag (and an anchor) outside the table row, which shouldn't be done in a table.
If you want something done when clicking a table, rather add an action to the row. Something like the following should work.
<tr {{action 'details' on="click"}}>
details will, of course, be a function in the corresponding controller from where you can transition to wherever you like.

how do you catch a 'click' on a {{linkTo}} using a view?

(Note: I am using Ember version 1.0.0-rc.3)
I'm trying to catch the 'click' of a {{linkTo}} using a view, so that I can do additional stuff (basically scroll the list of users in the sidebar) besides merely loading the new template. Me being relatively new to this (but having read the documentation!), I thought the following would just work:
"users" template:
{{#each user in users}}
{{#view App.ClickView}}
{{#linkTo user user}}{{ user.name }}{{/linkTo}}
{{/view}}
{{/each}}
the view code:
App.ClickView = Ember.View.extend({
click: function(evt) {
// do stuff
}
});
and for context, the layout template:
<div id='sidebar'>
{{#each user in users}}
{{#linkTo user user}}{{ user.name }}{{/linkTo}}
{{/each}}
</div>
<div id='main'>
{{ outlet }}
</div>
Referring back to the users template, you can see that each {{linkTo}} is contained within a view. I'm expecting for a click on that {{linkTo}} to therefore bubble up to, and caught by the view (App.ClickView). Unfortunately, it doesn't. It seems like the click is somehow not being bubbled up to the view when it's happens on a {{linkTo}}... What should I do?
Note #1:
If I replace the {{linkTo}} (only in div#main! I don't intend to replace the ones in div#sidebar) with an <a> element, it works, and the click gets caught by the view. However, I'm not so sure that i want to go down this route (I'd have to replicate the functionality of the {{linkTo}}!). And I'm thinking that there ought to be a better way to do this. Is there?
Note #2:
*Note that I'm also aware that i can put my intended "do stuff" code in renderTemplate() of the UserRoute, but the problem with that is that the effect will happen for every link to that route (including the ones in the sidebar - which is not what I want). I want the scroll to only trigger for specific {{linkTo}}s - specifically the {{linkTo}}s in div#main.
I would suggest using an anchor (<a>) tag with {{action ... target="view"}} in it instead of linkTo, apply your conditional logic in the view, and then if appropriate, re-send to the controller (this.get('controller').send(actionName), let it bubble to the router, and do a transitionTo in a router event handler.