I am using Ember.js to create a table from a json feed. I have a requirement to flash the values (within a table row) that are changing. How do I do this in Ember.js?
My template is shown below:
<tbody>
{{#each Quotes.quotesController.content}}
<tr>
<td class="quote-name">{{name}}</a>
<td class="quote-code">{{code}}</a>
<td class="quote-value">{{value}}</a>
<td class="quote-bid">{{bid}}</a>
<td class="quote-offer">{{offer}}</a>
</tr>
{{/each}}
</tbody>
Here's the code that fetches and updates the quotes:
fetchQuotes: function() {
var scope = this;
$.getJSON('/rest/quotes', function(data) {
$.each(data, function(index, quote) {
scope.updateQuote(quote);
});
});
}
Just as a comparison, in backbone.js, I was binding to change events at the attribute level and firing animations in the callbacks. For example:
this.model.bind('change:value', this.updateValue);
updateValue: function() {
var td = $(this.el).children('.quote-value');
td.html(this.model.get('value'));
td.animate({color: animationColor}, 200)
.animate({color: '#000000'}, 3800, 'easeInQuint');
},
I don't know if such an approach is possible/recommended using Ember.js.
Edit:
I almost have it now. I can handle an event on a table cell and change its color to anything. See this jsfiddle - click of any cell in the value column and it will turn red. The only thing I don't know is how to capture a change to the table cell value instead of a click event. Is there any such thing as a change event that I can capture?
See the following jsFiddle (a modification of an answer another one of your questions). I think from there you can do what you need, but in short you need to make an observer that watches for changes to the property on your controller that is holding the array that you are modifying.
Edit in response to the 1st comment:
Here is a working solution in response to that comment: http://jsfiddle.net/ud3323/ykL69/
Related
Summary
I have a problem with a list displayed by Ember which keeps displaying extra rows each time it is visited. The extra rows are duplicates of those which were initially displayed.
Detail
In an Emberjs 2.13.0 app I have a model that looks like this :
import DS from 'ember-data';
export default DS.Model.extend({
cceIdentifierParent: DS.attr('string'),
cchCceIdParent: DS.attr('string'),
nodeType: DS.attr('number')
});
I have a route, 'diagcctreetoplevelonly', which looks like this :
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return this.store.findAll('diagcctreetoplevelonly');
}
});
And a template that looks like this :
{{diag-warningbanner}}
{{#if model.length}}
<table>
<thead>
<tr>
<th>
cceIdentifierParent
</th>
<th>
cchCceIdParent
</th>
<th>
nodeType
</th>
</tr>
</thead>
<tbody>
{{#each model as |treenode|}}
<tr>
<td>
{{treenode.cceIdentifierParent}}
</td>
<td>
{{treenode.cchCceIdParent}}
</td>
<td>
{{treenode.nodeType}}
</td>
</tr>
{{/each}}
</tbody>
</table>
{{else}}
<p id="blankslate">
No Tree Nodes found
</p>
{{/if}}
{{outlet}}
That works fine the first time that 'diagcctreetoplevelonly' is visited - 12 rows are rendered - but on subsequent visits (without the underlying data having changed) the table rendered by the template has 12 extra rows for each time it has been visited.
Can anyone explain what i'm doing wrong ? Thank you.
EDIT: Thanks to the input from #Jeff and #Subtletree I was able to resolve this.
The problem was that the data returned had no 'id' attribute and when I created one the problem went away.
Because of the peculiar nature of the data it didn't actually matter what the id was and I didn't want to make changes to the backend so I created an id dynamically once the data had arrived on the client by creating a model level serializer and overriding the extractId method like this :
import ApplicationSerializer from './application';
export default ApplicationSerializer.extend({
extractId(modelClass, resourceHash) {
var arrId = [];
arrId.push(resourceHash['attributes']['cceIdentifierParent']);
arrId.push(resourceHash['attributes']['cchCceIdParent']);
arrId.push(resourceHash['attributes']['nodeType']);
var id = arrId.join('|');
return id == null || id === '' ? null : id+'';
},
});
It wouldn't have worked in all (perhaps most ?) situations but for my case this was good enough and resolved the problem.
To provide credit where it's due I got the idea for how to do this from the answer by #Casey here https://stackoverflow.com/a/35738573/364088 .
When ember-data receives records from a server it tries to match them to records already in the store by their id. If no id's are present then it can't find a match so instead of updating them it will just add them.
You could add an id to each record or could fetch the data with ajax and not use ember-data for this model.
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();
}
}
I am using Ember Version 2.5.1
I have setup checkbox, and they work when selected. But, when I select one box, for one user, all the boxes become selected for all the users. E.g if I select a team (Speedy) for Sophie, then everyones checkboxes end up checked and being on team Speedy. How do i stop this? And make it so each person can be on a different team.
e.g. the out come is a list of everyones names, (then if 'Team Speedy' is checked), next to everyones name is 'Team Speedy'.
teams.hbs;
<div class="container">
<table class="table">
<thead class="thead-inverse">
<tr>
<th>Name</th>
<th>Team Speedy</th>
<th>Team Alpha</th>
<th>Team Invincible</th>
</tr>
</thead>
<tbody>
{{#each model as |signed-player|}}
<tr>
<td> {{signed-player.name}} </td>
<td> {{input type='checkbox' checked=speedy}} </td>
<td> {{input type='checkbox' checked=alpha}} </td>
<td> {{input type='checkbox' checked=invincible}} </td>
</tr>
{{/each}}
</tbody>
</table>
{{#each model as |signed-player|}}
<p><b>{{signed-player.name}}</b> is on the following team: <b>{{teamName}}</b></p>
{{/each}}
</div>
controllers/team.js
speedy: false,
alpha: false,
invincible: false,
teamName: function()
{
if ( this.get('speedy') ) {
return 'Team Speedy';
}
else if ( this.get('alpha') ) {
return 'Team Alpha';
}
else if ( this.get('invincible') ) {
return 'Team Invincible';
}
else {
return 'No team yet';
};
}.property('speedy', 'alpha', 'invincible'),
The main problem I see in your example, is you are not setting any properties on any of the players. You are computing only one property {{teamName}} and just displaying it once per player, which is why clicking one checkbox appears to change the team for everybody... when in reality you are not actually setting any players onto any teams at all.
The other problem i see: you need to decide if chackboxes are the right UI for your selection. can a single player be on more than one team? if so, then checkbox is fine.. if not, then a select box or a set of radio inputs are probably better.
All that being said... each player object/record needs a property that actually stores the value representing which team(s) they belong to.
You will also need an action from each checkbox/radio/select/whatever to actually set the selected value onto the player object:
Here is my example:
https://ember-twiddle.com/1fe44ad4dc0494973c494a624ead79b7?fileTreeShown=false&openFiles=templates.application.hbs%2C
there are a number of things going on here... but essentially what we are doing is using a select input of choice (for me.. radio) and using the change action from the input to set each specific player's team property... which is defined on each player object to begin with.
I use a mut helper in my action in the example.. is is a quick way to set a value in a template action.. if you wanted to use a checkbox instead.. you will most likely want to replace the mut helper and apply your own custom action in a controller.
Please follow up if you need/want any extra explianation
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.
I have a very simple requirement but like with many things in Ember.JS, I'm banging my head against the wall trying to get it implemented.
I have an overview screen where a couple of records are displayed in a table.
To render the overview screen I'm using the following Route
App.LocationsIndexRoute = Ember.Route.extend({
setupController: function(controller) {
var locations = App.Location.find();
controller.set('content', locations);
},
renderTemplate: function() {
this.render('locations.index',{into:'application'});
}
});
This is working fine.
I would now like to conditionally render the overviewtable.
If records are present render the table.
If no records are present display a message.
I tried implementing this using the following controller.
App.LocationsIndexController = Ember.ArrayController.extend({
locationsPresent: function() {
var model = this.get('content');
return model.content.length > 0;
}.property()
});
and the following template
{{#if locationsPresent}}
<table class="table table-hover">
<tr>
<th>Latitude</th>
<th>Longitude</th>
<th>Accuracy</th>
<th></th>
<th></th>
</tr>
{{#each location in model}}
<tr>
<td>{{location.latitude}}</td>
<td>{{location.longitude}}</td>
<td>{{location.accuracy}}</td>
<td>{{#linkTo locations.edit location}}Edit{{/linkTo}}</td>
<td><button {{action removeItem location}}>Delete</button></td>
</tr>
{{/each}}
</table>
{{else}}
No locations present.
{{/if}}
The computed locationsPresent property is called once, before the page is rendered. At that time I assume that the model is still being loaded as the length = 0.
When the page is rendered, the locations from the App.Locations.find() are available but the locationsPresent is not called anymore, meaning the page decided to render the No locations present. message.
I went through the Managing Asyncrony in Ember page and assumed that the computer property locationsPresent would be updated if the underlying model changed (if it was completely loaded) as the page states :
Using a computed property for author eliminated the need to explicitly invoke the computation in a callback when the underlying property changed.
I'd love to know what I'm doing wrong and how I can fix this but more importantly why I seem to be missing some of these core concepts of Ember.JS. If somebody can point me where in the docs / guides this is explained properly I'd love to know.
I think it is a easy fix. You need to add the property you are observing. like so:
locationsPresent: function() {
var length = this.get('content.length');
return length > 0;
}.property('content.#each')
adding the #each is necessary if locationsPresent needs to recalculate wen content is added. I think you can also observe 'content.isLoaded'