In Ember.js why does 'afterRender' not trigger on a redraw? - javascript

I'm trying to call a function when new data gets added to a table in a view, but it's not working. I'm using Ember.js 2.5
I have two models, client and user. A client has many users.
My view to list clients looks like this:
<table class="table table-striped" id="admin-table">
<thead>
<tr>
<th>Primary User</th>
<th>User Email</th>
<th>Join Date</th>
</tr>
</thead>
<tbody>
{{#each model as |client|}}
<tr>
<td>{{client.primaryUser.name}}</td>
<td>{{client.primaryUser.email}}</td>
<td>{{client.createdAt}}</td>
</tr>
{{/each}}
</tbody>
</table>
primaryUser is a computed property containing the first user for each client.
primaryUser: Ember.computed('users', function(){
return this.get('users.firstObject');
})
Problem
I'm using the jquery tablesorter library which adds sorting and filtering to the table. Because the primaryUser get's loaded by AJAX I need to call $('#admin-table').trigger('update') when new data is added in order to have the library index the new information.
Here's what I'm doing:
modelObserver: function() {
Ember.run.next(this, this.updateTable);
}.observes('model.#each.primaryUser')
I've tried both run.next, and run.scheduleOnce('afterRender'..) and the result is always the same. The updateTable function triggers before all of the primaryUser objects are rendered.
Here's what happens if I put a debugger within this.updateTable:
it only triggers once, when a cached user (me) is rendered. The empty cells in this table populate a few ms later when the rest of the user information is ajaxed in, but updateTable never re-runs.
I can confirm at this point that the other primary user fields are not in the DOM:
Help
Is there a way to trigger an event once all objects have been rendered? I've using Ember.run.sync() as mentioned in this answer, but it didn't help.

Everything's fine but you need to manually check if all users are rendered. There's no other option. The code would look like this I think. It's a bit primitive, but I think you get the idea that you just can check the DOM.
modelObserver: function() {
var areRendered = [];
Ember.$('table#admin-table tr').each(function (index, element) {
$(element).find('td').each(function (anotherIndex, anotherElement) {
if (anotherIndex <= 1) { //This is because according to your screenshot you don't have to check all the cells, just first two if I'm not mistaking.
if (anotherElement.text()) {
areRendered.push(true);
} else {
areRendered.push(false);
}
}
});
});
if (!areRendered.contains(false)) { //Checking if there were any element without data.
Ember.run.next(this, this.updateTable);
}
}.observes('model.#each.primaryUser')
Hope it helps!

Related

How to add a dynamic row on click of button in a table?

I am new to the react Js as well front end. Here, I have a table which is like
<div className="col-xs-12 " id="scrollableTable">
<Table striped condensed hover id="jobData">
<thead style={backgrounColour}>
<tr>
<th className='serial-column'>Sr.No.</th>
<th className='company-column'>Company Name</th>
<th className='technology-column'>Technology</th>
<th className='job-column'>Job Title</th>
<th className='total-score-column'>Total Score</th>
<th className='average-score-column'>Average Score</th>
</tr>
</thead>
<tbody>{this.props.jobData.map(function (item, key) {
return (
<tr key={key}>
<td><b style={compName}>{item.id}</b></td>
<td><b style={compName}>{item.attributes.companyName}</b></td>
<td>Xamarian Developer</td>
<td>{item.attributes.name}</td>
<td><b style={compName}>30</b></td>
<td><b style={compName}>30</b></td>
</tr>
)
})}</tbody>
</Table>
</div>
I do have one button which is like
<button className = "btn btn-default">Add Row </button>
It looks like
Now, What I want to have is that on click of this button,
This should get added as a first row in this table.
Actually, I want to have an idea how can I do this? I tried by adding a static row in this but It did not work. So, I am asking this question.
Any help will be great for me.
FOR SINGLE EDITABLE ROW
Create separate component holding the row you would like to add, after clicking the button.
Add state property that will be used to define if your 'edition row' is visible.
It should have default state false (as it should not be visible at initial render)
this.state = {
isRowAddingEditorVisible: false
}
When "Add row" button is clicked, set state of that above property, to true
this.setState({ isRowAddingEditorVisible: true });
And in your component that holds whole table implement it like this:
<tbody>
{this.state.isRowAddingEditorVisible && <RowAddingEditor />} // <--- HERE
{this.props.jobData.map(function (item, key) {
return (
<JobTableRow key={key} data={item}>
)
})}
</tbody>
This way you dont need to modify original data that forms the table.
And JobTableRow would hold everything you have as row currently.
It provides better separation, readability and your closer to SRP (Single Responsibility Princple - code/method/component doing one thing only)
Check this fiddle to get the idea:
https://jsfiddle.net/n5u2wwjg/167786/
Also {this.state.isRowAddingEditorVisible && <RowAddingEditor />} - this is called short circuting.
Am using the fact that $booleanValue && <ReactComponent/> will evaluate to <ReactComponent> when $booleanValue is true and will evaluate to nothing (visually speaking) when $booleanValue is false
FOR MULTIPLE EDITABLE ROWS
https://jsfiddle.net/n5u2wwjg/173218/
Hint 1:
I used Math.random() to get unique IDs for rows. It is just simple, fast, example solution and in that form should not be used in production code.
Usually unique IDs generators are based on Math.random(), but randomized bit more with some additional code.
Hint 2:
Remember that when POSTing row to server, you also need to remove its ID from state.
Hint 3:
Usually when you have method starting with get and returning some DOM elements you use in render method, it is signal that contents of this method should be moved to separate React component.
Hint 4:
In above example I use ES6 syntax. Learn it if you don't know it yet, it's your friend :). Search "ES6 features tutorial".
spread operator - ...
arrow function - => (anonymous also - not stored anywhere or named)
First you have to keep the this.props.jobData in state, Then OnClick of the button you have to update this.state.jobData
add a field in item to identify if it's an editable row or non editable. Let's say isEditable
The code will be:
<tbody>{this.state.jobData.map(function (item, key) {
if (!item.isEditable) {
return (
<tr key={key}>
<td><b style={compName}>{item.id}</b></td>
<td><b style={compName}>{item.attributes.companyName}</b></td>
<td>Xamarian Developer</td>
<td>{item.attributes.name}</td>
<td><b style={compName}>30</b></td>
<td><b style={compName}>30</b></td>
</tr>
)
} else {
return (
//Add your editable row here
)
}
})}</tbody>
#azrahel gave right answer about first problem (modify table view), below I'm writing about how it should work later - action triggered from that 'mini form'.
In short - it's data driven. Update data, view will be updated.
this.props.jobData.map shows that rows are from this.props.jobData array. Add row means then add to array. There is one problem - you can't mutate props. Props are passed from parent component then you have to update data in parent. To handle this you should have addJob method in parent and pass this handler as prop (like jobData, read docs).
But ... probably this array is not a local data, was fetched from server. If you want to update this on server (push to DB, to be available to others) then yon need to post data to server (it will store it in DB). In simplest scenario post request will return new, updated array. Updated data, passed to component should render component with new array (with new row). It's up to you if 'editing row' should be still visible or not.

Get table attribute or property value within ember controller

I have an ember application which has a table and a grid component within a page. I have enabled drag and drop feature which drags a value from a particular cell of a table and drops it into the empty space of the grid. As the tables have more than one column, I want to get the index position or I want to know which column's cell is being dragged. I want that index value of column within the controller.
Suppose a table has 3 rows with 3 columns. For all the elements within the first column being dragged, I want to get the index value as 1 similarly for 2nd and 3rd column.
Here is my .hbs code for the table
<table class = "table table-bordered">
<thead>
<tr>
<th colspan="6">Inbound Units</th>
</tr>
</thead>
<tbody>
{{#each currentbid as |currentbid|}}
<tr>
{{#each pull as |pull|}}
{{#if (eq pull.DRIVERNAME currentbid.DRIVERNAME)}}
<td abbr="P1">{{#draggable-object content=pull position=1 dragEndAction='dragEndAction'}}{{#draggable-object-target action="draganddrop"}}{{pull.P1}}{{/draggable-object-target}}{{/draggable-object}}</td>
<td>{{pull.P2}}</td>
<td>{{pull.P3}}</td>
<td>{{pull.P4}}</td>
{{/if}}
{{/each}}
</tr>
{{/each}}
</tbody>
</table>
As you can see wihtin the table td tag, I have specified abbr attribute. But I have no idea how to get the value of abbr within the controller. Any other way of getting this is also fine.
Thanks !
This answer applies to Ember 2.x.x and was written as of 2.15.
By default, actions assigned to a specific event via closure, like ondragEnd={{action "someAction"}} receive the event target as an argument:
actions: {
someAction(event) {
console.log(event.target)
}
}
Possibly, you could use event.target.parentElement.className in your component to get the class name, then send the action and argument to your controller. Hopefully that selector will return the new parent and not the old one.
You can read about different ways to catch browser events in the Ember Guides.

Why AJAX response is not in sortable manner

I am using sorttable.jsfor table sorting and my table is updated in every 3 sec by ajax response but the response is not in sorted manner as i expect it to be.
Index page
<div id="resDiv">
<table id="myTable1" class="sortable">
<thead>
<tr><th id="person">Person</th><th id="monpay">Monthly pay</th></tr>
</thead>
<tbody>
<tr><td>Jan Molby</td><td>£12,000</td></tr>
<tr><td>Steve Nicol</td><td>£8,500</td></tr>
<tr><td>Steve McMahon</td><td>£9,200</td></tr>
<tr><td>John Barnes</td><td>£15,300</td></tr>
</tbody>
<tfoot>
<tr><td>TOTAL</td><td>£45,000</td></tr>
</tfoot>
</table>
</div>
Append new table data
ajax response is :
<table id="myTable" class="sortable">
<thead>
<tr><th>Person</th><th>Monthly pay</th></tr>
</thead>
<tbody>
<tr><td>prabha Molby</td><td>£12,000</td></tr>
<tr><td>abcd Nicol</td><td>£8,500</td></tr>
<tr><td>steev McMahon</td><td>£9,200</td></tr>
<tr><td>John Barnes</td><td>£15,300</td></tr>
</tbody>
<tfoot>
<tr><td>TOTAL</td><td>£55,000</td></tr>
</tfoot>
</table>
JavaScript
$(function() {
$("#ajax-append").click(function() {
setInterval(function() {
var request = $.get("assets/replacecontent.jsp", function(html) {
alert(html);
$('#resDiv').html(html);
var newTableObject = document.getElementById("myTable");
alert(newTableObject);
sorttable.makeSortable(newTableObject);
// alert($("#myTable").length);
});
}, 3000);
});
});
Now if any time i sort the ajax response it get sorted but after another response it again change it's order but i want it sorted as previous one.
I think you should have read what the sorttable.js faq says:
Sorting the table when the page is loaded
Lots of people ask, "how do I make sorttable sort the table the first
time the page is loaded?" The answer is: you don't. Sorttable is about
changing the HTML that is served from your server without a page
refresh. When the page is first served from the server, you have to
incur the wait for it to be served anyway. So, if you want the table
sorted when a page is first displayed, serve the table in sorted
order. Tables often come out of a database; get the data from the
database in a sorted order with an ORDER BY clause in your SQL. Any
solution which involves you running sorttable as soon as the page
loads (i.e., without user input) is a wrong solution.
But they also state a solution for that:
//Find the TH you want to use, maybe you can store that using an event handler before
var myTH = document.getElementsByTagName("th")[0];
//Then sort it
sorttable.innerSortFunction.apply(myTH, []);
But to that you will have to find the column your user clicked on before, and to be honest I have not found any way using the sorttable api directly. Maybe use some kind of click event handler and store the th that was clicked last.

Insert an image depending on table content

I have a database item (called fairtrade) that has one of the following values: Yes, No, N/A.
These are displayed in the front end in a <table>.
<table class="data-table" id="product-attribute-specs-table">
<colgroup>
<col width="25%"></col>
</colgroup>
<tbody>
<tr class="last odd">
<th class="label">Fairtrade</th>
<td class="data last">N/A</td>
</tr>
</tbody>
</table>
If the database displays YES in the content I would like to display an image. If the database displays No or N/A I would like to hide that particular table row.
Can I use Javascript / JQuery to make the above happen. I'm not even sure if its possible.
Any help would be appreciated.
Well, you will need something like PHP to fetch the data from the database since JavaScript alone is not capable of doing that.
When that has been done you can use the server side language to insert the images depending on what data you get from the database while it's rendering the table.
If the table data is rendered for you you can use JavaScript to show the image depending on a value in a cell.
A simple jQuery example would probably be something like this (sorry, not tested, from the top of my head so might contain errors)
$("td").each(function(){
if($(this).text() == "YES"){
//code to show image here
}
});
Given your HTML, the following code should work for you.
$('.data-table tr .data').each(function() {
if ($(this).text() == 'Yes') {
// show your image
}
else {
$(this).closest('tr').hide();
}
});
Example fiddle

jQuery cleaning HTML table

This is my table:
<tr class=stuff>
<td id=id></td>
<td id=city_id></td>
<td id=temp></td>
<td id=date></td>
</tr>
This is my Javascript:
<script>
$(document).ready(function() { // waits when document is ready
$('.data').change(function() { // when dropbox value changes do this
getWeather(); // here I tried inserting table clearing code
});
});
function getWeather() {
$.getJSON('getTemperature/' + $('.data option:selected').val(), null, function(data) { // JSON request
$("#id").text(data.id); // changes fields accordingly
$("#city_id").text(data.city_id);
$("#temp").text(data.temperature);
$("#date").text(data.date);
});
}
</script>
Every item in dropdown menu does not have response from server, so I want it to clear the table just before making a new JSON request. So when JSON comes back with data, data is updated accordingly, but when JSON comes back with nothing, then all the tables will be empty.
At the moment when JSON retrieves no data, the old data still remains in the table.
I tried using $('.stuff').remove() and $('.stuff').clean() , but after using them right before getWeather(); then later I wasn't able to put info into table which I received from JSON. It just did not work anymore.
Feel free to ask any questions.
Try this
$('.stuff td').text("");
getWeather();
Depending how much of this sort of thing you will be doing on your site you might want to look into KnockoutJS, it is designed for dynamic displays with changing data, including auto hiding sections.

Categories

Resources