I am performing some ASP.NET gridview conversions using the datatables.net plug-in. The answer to why I am doing this is more involved and can be debated. However, I need some help with one of the issues I am running into.
Using Javascript to convert the gridview on the page was actually quite simple and works well. The major issue is that I want to have a fixed 'total' row (footer) within the body of the datatable so that it remains responsive just like the rest of the table.
I have attempted to add a footer using the code-behind, and I can populate that footer with total data, but it is not responsive with the rest of the table. I am assuming because the <tfoot> is outside of the <tbody>.
Using javascript, I have successfully added a new datatable row and I can output the data to the console, but I am unable to populate the added row with the object data.
Javascript:
var sum;
$(document).ready(function () {
var table = $('#cphPage_gvTaxColl').DataTable();
//convert string to int
var intVal = function (i) {
var j = $("<span/>");
var txt = j.html(i).text();
// alert('txt :' + txt);
var myVal = typeof txt === 'string' ?
parseFloat(txt.replace(/[^\d.-]/g, '')) :
typeof txt === 'number' ?
i : 0;
return myVal || 0;
};
//format integer as currency
var formatSum = function (myVal) {
return accounting.formatMoney(myVal, {
symbol: "$",
precision: 2,
thousand: ",",
decimal: ".",
format: {
pos: "%s %v",
neg: "%s (%v)",
zero: "%s 0.00"
}
});
};
//add total row and determine index
table.row.add(['GRAND TOTAL']).draw();
var total_row = (table.row().count() + 1);
var total_col = (table.row(total_row).column().count + 1);
//alert
console.log('total row: ' + total_row);
//loop columns
table.columns('.sum').every(function () {
sum = this
.data()
.reduce(function (a, b) {
console.log('adding ' + intVal(a) + ' and ' + intVal(b));
return intVal(a) + intVal(b);
});
//alert
console.log('sum: ' + sum);
console.log('column row 2 val: ' + this.data().row([2]));
$(this.cell.node( total_row )).html(
formatSum(sum)
);
});
});
How do I present the object data within the datarow?
I am also receiving the following error message, and I am not certain which parameter is missing ( the 2nd and 3rd columns are null ):
Error message
I have included a screenshot with the console data, and if you need it I can provide the .aspx markup:
Page + cosole log output
I'm still learning the ins-and-outs of this stuff. Any guidance you can provide is greatly appreciated.
Thanks in advance!
Here is my solution:
The html-table for datatables should have <tfoot>
Something like this:
<table id='table'>
<tbody>
<tr><td></td></tr>
</tbody>
<tfoot>
<tr><td></td></tr>
</tfoot>
</table>
Define footerCallback field
Datatables initialization:
$('#table').dataTable({
...
"footerCallback": _footerDrawn
...
});
footerCallback:
I use server-side data, so my source of data for footer is just another field of of ajax-response:
_footerDrawn: function( row, data, start, end, display ) {
var table = $('#table').dataTables();
var json = table.api().ajax.json();
// assign values to each cell of the footer from json.total array
$.each( table.api().columns().indexes(), function( index ) {
$( table.api().column( index ).footer() ).html( json.total[index] );
});
}
}
json.total contains array of string to print in footer row
Related
We have a JQuery datatable with excel export, but cant solve a problem with numbers.
Numbers displays in the datatable in hungarian format: 5 588,9906 (whitespace is the thousand separator, comma is the decimal point).
Now we need to display the datas as numbers in excel, but thats not working every time. In excel settings, the thousand separator is whitespace, the decimal point is comma.
Datatable:
datatable format
Result in Excel (lower one is ok, upper one is a string):
excel error
The code:
var buttonCommon = {
exportOptions: {
format: {
body: function ( data, row, column, node ) {
return column === 6 || column === 8 || column === 9 || column === 10 || column === 11 || column === 12 || column === 13
? data.replace(',', '.').replace(' ', ',') : data;
}
}
}
};
var table = $('#talaltszamlak').DataTable({
dom: 'Blfrtip',
buttons: [
$.extend( true, {}, buttonCommon, {
extend: 'excelHtml5'
} ),
],
pageLength: 50,
"order": [[ 3, "asc" ]],
language: {
url: '//cdn.datatables.net/plug-ins/1.10.22/i18n/Hungarian.json'
},
});
Thank You!
Here is an example where you provide your own custom Excel number format.
In this case, the Excel format string is:
#,##0.0##
So, we will get up to 3 decimal places (and a minimum of 1 decimal place).
The test data:
<div style="margin: 20px;">
<table id="example" class="display dataTable cell-border" style="width:100%">
<thead>
<tr>
<th>Name</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
<tr><td>Tiger Nixon</td><td>123,45</td></tr>
<tr><td>Garrett Winters</td><td>4 567,892</td></tr>
<tr><td>Ashton Cox</td><td>1 233 445,1</td></tr>
</tbody>
</table>
</div>
The DataTable with custom code:
$(document).ready(function() {
var table = $('#example').DataTable( {
dom: 'Brftip',
buttons: [
{
extend: 'excelHtml5',
text: 'Excel',
exportOptions: {
format: {
body: function ( data, row, column, node ) {
return reformatNumber(data, row, column, node);
}
}
},
customize: function( xlsx ) {
addCustomNumberFormat(xlsx, '#,##0.0##');
formatTargetColumn(xlsx, 'B'); // Excel column B
}
}
]
} );
} );
function reformatNumber(data, row, column, node) {
// replace spaces with nothing; replace commas with points.
if (column === 1 ) {
var newData = data.replace(',', '.').replaceAll(' ', '');
return newData;
} else {
return data;
}
}
function addCustomNumberFormat(xlsx, numberFormat) {
// this adds a new custom number format to the Excel "styles" document:
var numFmtsElement = xlsx.xl['styles.xml'].getElementsByTagName('numFmts')[0];
// assume 6 custom number formats already exist, and next available ID is 176:
var numFmtElement = '<numFmt numFmtId="176" formatCode="' + numberFormat + '"/>';
$( numFmtsElement ).append( numFmtElement );
$( numFmtsElement ).attr("count", "7"); // increment the count
// now add a new "cellXfs" cell formatter, which uses our new number format (numFmt 176):
var celXfsElement = xlsx.xl['styles.xml'].getElementsByTagName('cellXfs');
var cellStyle = '<xf numFmtId="176" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1"'
+ ' applyFont="1" applyFill="1" applyBorder="1"/>';
// this will be the 8th "xf" element - and will therefore have an index of "7", when we use it later:
$( celXfsElement ).append( cellStyle );
$( celXfsElement ).attr("count", "69"); // increment the count
}
function formatTargetColumn(xlsx, col) {
var sheet = xlsx.xl.worksheets['sheet1.xml'];
// select all the cells whose addresses start with the letter prvoided
// in 'col', and add a style (s) attribute for style number 68:
$( 'row c[r^="' + col + '"]', sheet ).attr( 's', '68' );
}
The code adds a new number format record to the Excel styles XML sheet; it then uses that record to create a new cell format record. Finally, it locates every cell in column B of the Excel spreadsheet and applies the cell formatter.
The end result is that a value which is displayed in the DataTable like this:
1 233 445,1
Will be displayed in Excel like this:
1,233,445.1
You can use whatever Excel number format string you want, instead of #,##0.0##.
I an trying to determine how to remove one or more specific rows from an array of objects.
In the page, I have a text box for user to enter a user ID; click "Find" button and I do a search in AD and add a row to a table with user's display name and user ID. In addition to this, for later processing, I store in an array of objects some info on user in addition to display name and user ID.
Also, I have a checkbox column in above table that user can select and remove one or more of the added users; I can correctly rebuild the table displaying data but don't know how, at this point, remove one or more rows from the array (i.e. the deleted users).
This is what I have:
HTML:
<table class="adminList" style="width:100%">
<thead>
<tr>
<th></th>
<th>User Name</th>
<th>User ID</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<button type="button" class="btn-primary btn-sm delete-admin-row">Delete User(s)</button>
JS/jQuery:
var admins = [];
<Somehow, using ajax, get user's data from AD>
var fn = userData.FirstName;
var mi = userData.Initial;
var ln = userData.LastName;
var name = userData.DisplayName;
var email = userData.Email;
var userid = userData.UserID;
//push the object onto the array
admins.push({
"fn" : fn,
"mi" : mi,
"ln": ln,
"email": email,
"userid": userid
});
// Display in admins list
var markup = "<tr><td><input type='checkbox' name='record'></td><td>" + name + "</td><td>" + userid + "</td></tr>";
$(".adminList tbody").append(markup);
FURTHER DOW IN THE CODE:
$(document).ready(function(){
$(".delete-admin-row").click(function(){
$(".adminList tbody").find('input[name="record"]').each(function(){
if($(this).is(":checked")){
$(this).parents("tr").remove();
// HERE I NEED TO POP THE ARRAY
}
});
});
});
Not sure if removing the checkbox column and having instead an 'x' image next to each row, associate some property of user data with this 'x' image, say User ID, as 'data-xxx' and accessing it in delete function would be easier; but I still wouldn't know how to access and delete the row from array based on 'userid'.
Update
Following the suggestions made, I made the changes but noticed that IE does not understand => nor findIndex. So, I made the following changes but still get -1 as returned index.
$(".delete-admin-row").click(function(){
$(".adminList tbody").find('input[name="record"]').each(function(){
if($(this).is(":checked")){debugger
$(this).parents("tr").remove();
// Both of these return the userid
const useridToFind1 = this.dataset.userid;
const useridToFind = $(this).closest("tr").find("td:eq(2)").text();
//const index = admins.findIndex(admin => admin.userid === useridToFind);
const index = admins.indexOf(function (e) { return e.userid === useridToFind });
if (index !== -1) { // Always -1
admins.splice(index, 1);
}
}
});
});
I'd add the UserID to a data attribute of the input in a TR, so that when iterating over checked inputs, you can take that value and use .findIndex to see if a matching UserID object exists in the array. Then you can splice it:
var markup = "<tr><td><input data-userid='" + userid + "' type='checkbox' name='record'></td><td>" + name + "</td><td>" + userid + "</td></tr>";
and
$(".delete-admin-row").click(function(){
$(".adminList tbody").find('input[name="record"]:checked').each(function(){
$(this).parents("tr").remove();
const useridToFind = Number(this.dataset.userid);
const index = admins.findIndex(admin => admin.userid === useridToFind);
if (index !== -1) {
admins.splice(index, 1);
}
});
});
I'm assuming the userid is a number, thus the need for a type cast in the .find (since a dataset element will always be a string).
If you have to use an obsolete 7-year-old browser, dumb down the syntax and use a different iteration method:
"use strict";
$(".delete-admin-row").click(function() {
$(".adminList tbody")
.find('input[name="record"]:checked')
.each(function() {
$(this)
.parents("tr")
.remove();
var useridToFind = Number(this.dataset.userid);
var foundIndex = -1;
admins.forEach(function(admin, index) {
if (admin.userid === useridToFind) foundIndex = index;
});
if (index !== -1) {
admins.splice(index, 1);
}
});
});
Just use splice method, what you need to know - index of array (first element - 0)
var a = [1, 2, 3, 4, 5];
a.splice(1, 2); // delete 2 elements starting from second element
// (second, because first element in array has index - 0,
// so index 1 - it's second element)
// a will contain -> [1, 4, 5]
Get the user ID from the third <td> in the current row. Find the index of the array element with that userid, and remove it with splice().
$(document).ready(function() {
$(".delete-admin-row").click(function() {
$(".adminList tbody").find('input[name="record"]').each(function() {
if ($(this).is(":checked")) {
let userid = $(this).closest("tr").find("td:eq(2)").text();
let index = admins.findIndex(el => el.userid == userid);
if (index >= 0) {
admins.splice(index, 1);
}
$(this).parents("tr").remove();
}
});
});
});
I have a dynamically generated CSV file from another vendor that I am puling in and need to show in a table on my site. The problem is I need to be able to manipulate the data from the CSV so it can show the corrected values in the html table. In the end I need the HTML table to just display the Products, not the Mixed Sets.
I am using jquery and the papaparse library to get the data and parse it in a table in html. My codepen is here:
https://codepen.io/BIGREDBOOTS/pen/YQojww
The javascript pulls the initial csv values and display in a table, but I can't figure out how to to add together the values. If there is a better way of going about this, like converting the CSV to some other form of data like JSON, That is fine too.
My CSV looks like this:
product_title,product_sku,net_quantity
Product 1,PRD1,10
Product 2,PRD2,20
Product 3,PRD3,30
Mixed Set 1,MIX1,100
Mixed Set 2,MIX2,50
Mixed Set 3,MIX3,75
The Javascript I am using is:
function arrayToTable(tableData) {
var table = $('<table></table>');
$(tableData).each(function (i, rowData) {
var row = $('<tr class="rownum-' + [i] + '"></tr>');
$(rowData).each(function (j, cellData) {
row.append($('<td class="' + [i] + '">'+cellData+'</td>'));
});
table.append(row);
});
return table;
}
$.ajax({
type: "GET",
url: "https://cdn.shopify.com/s/files/1/0453/8489/t/26/assets/sample.csv",
success: function (data) {
$('body').append(arrayToTable(Papa.parse(data).data));
}
});
My rules for the mixed set:
Mixed Set 1 should add 100 to Product 1 and Product 2.
Mixed Set 2 should add 50 to Product 2 and Product 3.
Mixed Set 3 should add 75 to Product 1, Product 2 and Product 3.
I'd like to end up with Just the products output, and the correct numbers added to the formula.
The end result would be a table with Product 1 = 185, Product 2 = 245, and Product 3 = 155.
While it would be even better if the top THEAD elements were in a "th", It's fine if that is too complicated.
<table>
<tbody>
<tr class="rownum-0">
<td class="0">product_title</td>
<td class="0">product_sku</td>
<td class="0">net_quantity</td>
</tr>
<tr class="rownum-1">
<td class="1">Product 1</td>
<td class="1">PRD1</td>
<td class="1">185</td>
</tr>
<tr class="rownum-2">
<td class="2">Product 2</td>
<td class="2">PRD2</td>
<td class="2">245</td>
</tr>
<tr class="rownum-3">
<td class="3">Product 3</td>
<td class="3">PRD3</td>
<td class="3">155</td>
</tr>
</tbody>
</table>
Without knowing the size of the dataset you're working with, I suggest you first iterate through all the CSV dataset in order to populate a list of products with the correct values, and then iterate again on that to populate your HTML table:
function datasetToMap(data) {
var ret = {};
//Initialize a map with all the product rows
$(data).each(function(index, row) {
if(row[0].startsWith("Product")) {
ret[row[1]] = row; //Using the SKU as the key to the map
}
});
//Apply your mixed sets rules to the elements in the ret array
$(data).each(function(index, row) {
if(row[1] === "MIX1") {
ret["PRD1"][2] += 100;
ret["PRD2"][2] += 100;
}
//Do the same for Mixed sets 2 and 3
});
return ret;
}
function appendMapToTable(map) {
var $table = $('#my-table');
Object.keys(map).forEach(function(key, i) {
var rowData = map[key];
var row = $('<tr class="rownum-' + [i] + '"></tr>');
$(rowData).each(function (j, cellData) {
row.append($('<td class="' + [j] + '">'+cellData+'</td>'));
});
$table.append(row);
});
}
$.ajax({
type: "GET",
url: "https://cdn.shopify.com/s/files/1/0453/8489/t/26/assets/sample.csv",
success: function (data) {
appendMapToTable(datasetToMap(Papa.parse(data).data));
}
});
Note that this expects a table with id my-table to be already present in your HTML: you could manually parse the first row of your CSV data to add the table headings.
Also note that if your CSV dataset is very big this is definitely not an optimal solution, since it requires iterating through all its lines twice and then iterating again through all the list built with computed values.
I'm trying to use tablesorter on a dynamic HTML table. When the data in the table changes, and I try to sort, the table messes up; it adds the previous data and the current data and combines them both into one table, and it is also sorting other tables!! How can I make it so when the table changes, tablesorter will update? I'm populating the table from a .csv using jQuery, and the table is dynamic, because the HTML page the table is in has multiple file names, if you click on one, the data from that file will be loaded to that table
I'm using multiple tables, and one tablesorter, but here's one of my tables:
<div id="origtable">
<table border="1" id="table_side">
<thead>
<tr>
<th>Origin <br> Country</th>
<th>Count</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
Here's my tablesorter:
/* tried this at first because I'm waiting for data from .csv file to populate table
setInterval(function(){sortStuff()},1000);
function sortStuff(){
$("table").tablesorter({
sortList: [[1,1]]
});
$("table").trigger("update");
}
*/
$(document).ready(function(){
$("table").tablesorter({
sortList: [[0,1]]
});
});
$("table").on( "click", function() {
$("table").trigger("update");
});
EDIT 1:
jquery function used to populate html table after reading from .csv (this jquery code is in a seperate .js file)
function populateTableCounts(rowkey, tablename, hashdata)
{
var rowhtml = "";
$.each(hashdata, function (key, value) {
key = key.replace(/-/g, ' / ');
key = key.replace(/:/g, ' , ');
var rowdata=countrow.replace('$row_key', key);
rowdata=rowdata.replace('$row_val', value);
//add row <tr> to var rowhtml
rowhtml += rowdata;
});
//append populated rowhtml to TBODY
$('#' + tablename + ' tbody').append(rowhtml);
}
Ignore my comment is trigger("update") the function you need, but check if this code works
$(document).ready(function(){
$("table").tablesorter({
sortList: [[0,1]]
});
$("table").click(function(){ $("table").trigger("update"); });
});
EDIT
If you are using get to obtain csv data you need something like this:
$.get("your/csv/file.csv",function(csv){
//... code that handle your csv and put into table
//...
//AT END UPDATE TABLE
$("table").trigger("update");
});
EDIT2
function populateTableCounts(rowkey, tablename, hashdata)
{
var rowhtml = "";
$.each(hashdata, function (key, value) {
key = key.replace(/-/g, ' / ');
key = key.replace(/:/g, ' , ');
var rowdata=countrow.replace('$row_key', key);
rowdata=rowdata.replace('$row_val', value);
//add row <tr> to var rowhtml
rowhtml += rowdata;
});
//append populated rowhtml to TBODY
$('#' + tablename + ' tbody').append(rowhtml);
//You can do this
$('#' + tablename).trigger("update");
//or this if you want $('table').trigger('update');
}
You can do this things without problem, you need to take care that the elements must be loaded so execute all on $(document).ready.
There is no problem if the js script is on another file.
I produce an int from JSON data
var f_page = ["TheHouseofMarley"];
retrieveData(f_page[0]);
function retrieveData(teamName) {
var baseURL = 'http://graph.facebook.com/';
$.getJSON(baseURL+teamName+"&callback=?", function(data) {
$('#FBlikes').append(data.likes)
});
};
and this works, it gives ~ 8407
I have a chart that reads data from < table id="chartData">
Grabbing the data from the table
I use a jQuery selector — $('#chartData td') — to select all the data cells in the table. I can then iterate through these cells with the jQuery each() method. For each cell, I determine if it's a label (e.g. "SuperWidget") or a value (e.g. "FBLike") cell, based on whether it's in the left or right column. I then store the cell contents under the 'label' or 'value' key in an associative array, which we then place inside the chartData array.
$('#chartData td').each( function() {
currentCell++;
if ( currentCell % 2 != 0 ) {
currentRow++;
chartData[currentRow] = [];
chartData[currentRow]['label'] = $(this).text();
} else {
var value = parseFloat($(this).text());
totalValue += value;
value = value.toFixed(2);
chartData[currentRow]['value'] = value;
}
// Store the slice index in this cell, and attach a click handler to it
$(this).data( 'slice', currentRow );
$(this).click( handleTableClick );
The problem is when I insert this number into < table id="chartData"> it is not read by the chart!
<table id="chartData">
<tr style="color: #0DA068">
<td>Number of Likes </td><td><span id='FBlikes'></span> </td> //Not Read!
</tr>
<tr style="color: #194E9C">
<td>MegaWidget</td><td>20000</td> //This is Read by the Chart!
</tr>
In short: Javascript output is not being read from HTML table.
Could anyone point me in some direction? I'm really new at code.
Usually this problem occurs in Ajax.
Build a string appending "data.likes" to it. Then finally assign the string to the element.
This may sound absolutely stupid, but it worked for me. Whenever i use to build a table dynamically in jQuery using the ajax response string, i would never get a table. Then i followed the procedure I mentioned.
If my solution works, some one please help me understand why is it so.