jQuery datatable with custom search bars - javascript

I'm trying to search in the columns 'Part Number' and 'Internal Reference' with one searchbar and with another searchbar in the column named 'Description':
By the moment I'm trying with this in the html:
...
<th class="code">Part Number</th>
<th class="keyword">Description</th>
<th class="code">Internal Reference</th>
...
And this in the .js:
$('#keyword-search').keyup(function() {
var val = $.trim($(this).val()).replace(/ +/g, ' ').toLowerCase();
oTable.columns('.keyword').search('^' + val, true, false).draw();
});
$('#code-search').keyup(function() {
var val = $.trim($(this).val()).replace(/ +/g, ' ').toLowerCase();
oTable.columns('.code').search('^' + val, true, false).draw();
});
I want to be able to search in the 'Part Number' or in the 'Reference' column but not in both at the same time, just to show the columns where the expression exists in the first or in the second column.
I need this to be solved, I tried different ways and I don't find the solution.
EDIT 1: I want a final result like this:
But that search is not working, there is no final filtered result, there is a collision between the two columns because it's searching 'NUM1' in the column 'Part Number' and in the column 'Internal Reference' at the same time. I mean that the code it's doing an AND search instead of an OR search, that is what I want.

Something along the lines of this should work:
$('#keyword-search').keyup(function() {
var searchText = $(this).val().toLowerCase();
$.fn.dataTable.ext.search.push(function(settings, data, dataIndex) {
//Assuming part number and internal reference are 1st and 3rd columns
if((~data[0].toLowerCase().indexOf(searchText)) || (~data[2].toLowerCase().indexOf(searchText))) {
return true;
}
return false;
}
Check this and this

Related

JQuery Datatable Excel export number format

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##.

Selecting a row in table based on content and clicking link from selected row - Protractor

I have a page object that looks like this:
<table border>
<th>Email</th>
<th>action</th>
<tr current-page="adminUsers.meta.page">
<td>admin#example.com</td>
<td>Delete permanently</td>
</tr>
<tr current-page="adminUsers.meta.page">
<td>matilda#snape.com</td>
<td>Delete permamently</td>
</tr>
</table>
I want to create a method that will enable me to delete a user based on his email address.
This is what I came up with, basing on How to find and click a table element by text using Protractor?:
describe('Admin panel', function() {
it('admin deletes a user', function() {
var re = new RegExp("matilda#snape.com");
var users_list = element.all(by.xpath("//tr[#current-page='adminUsers.meta.page']"));
var delete_btn = element(by.xpath("//a[contains(text(), 'Delete permamently')]"));
users_list.filter(function(user_row, index) {
return user_row.getText().then(function(text) {
return re.test(text);
});
}).then(function(users) {
users[0].delete_btn.click();
});
// some assertion, not relevant right now
});
});
First I'm trying to filter the row in which there's a user I want delete (array with all rows fitting my filter, then selecting the first row - should be one row anyway) and then click corresponding Delete button.
However, from my debugging I know that the method ignores the filtering and clicks the first Delete button available in the table and not the first from filtered elements.
What am I doing wrong?
In this particular case, I would use an XPath and its following-sibling axis:
function deleteUser(email) {
element(by.xpath("//td[. = '" + email + "']/following-sibling::td/a")).click();
}
I agree with #alexce's short & elegant answer but #anks, why don't you delete inside your filter??
describe('Admin panel', function() {
it('admin deletes a user', function() {
var re = new RegExp("matilda#snape.com");
var users_list = element.all(by.xpath("//tr[#current-page='adminUsers.meta.page']"));
var delete_btn = element(by.xpath("//a[contains(text(), 'Delete permamently')]"));
users_list.filter(function(user_row, index) {
return user_row.getText().then(function(text) {
return if(re.test(text)) { //assuming this checks the match with email id
user_row.delete_btn.click();
}
});
})
// some assertion, not relevant right now
});
});

How to populate/present added datatables row with data?

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

JavaScript DOM not being read in html table

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.

Keep checkbox values on content update

I've been struggling with this issue for a while now. Maybe you can help.
I have a table with a checkbox at the beginning of each row. I defined a function which reloads the table at regular intervals. It uses jQuery's load() function on a JSP which generates the new table.
The problem is that I need to preserve the checkbox values until the user makes up his mind on which items to select. Currently, their values are lost between updates.
The current code I use that tries to fix it is:
refreshId = setInterval(function()
{
var allTicks = new Array();
$('#myTable input:checked').each(function() {
allTicks.push($(this).attr('id'));
});
$('#myTable').load('/get-table.jsp', null,
function (responseText,textStatus, req ){
$('#my-table').tablesorter();
//alert(allTicks + ' length ' + allTicks.length);
for (i = 0 ; i < allTicks.length; i++ )
$("#my-table input#" + allTicks[i]).attr('checked', true);
});
}, $refreshInterval);
The id of each checkbox is the same as the table entry next to it.
My idea was to store all the checked checkboxes' ids into an array before the update and to change their values after the update is done, as most of the entries will be preserved, and the ones that are new won't really matter.
'#myTable' is the div in which the table is loaded and '#my-table' is the id of the table which is generated. The checkbox inputs are generated along with the new table and with the same ids as before.
The weird thing is that applying tablesorter to the newly generated table works, but getting the elements with the stored ids doesn't.
Any solutions?
P.S: I know that this approach to table generation isn't really the best, but my JS skills were limited back then. I'd like to keep this solution for now and fix the problem.
EDIT:
Applied the syntax suggested by Didier G. and added some extra test blocks that check the status before and after the checkbox ticking.
Looks like this now:
refreshId = setInterval(function()
{
var allTicks = []
var $myTable = $('#my-table');
allTicks = $myTable.find('input:checked').map(function() { return this.id; });
$('#myTable').load('/get-table.jsp', null,
function (responseText,textStatus, req ){
$myTable = $('#my-table');
$('#my-table').tablesorter();
var msg = 'Before: \n';
$myTable.find('input').each(function(){
msg = msg + this.id + " " + $(this).prop('checked') + '\n';
});
//alert(msg);
//alert(allTicks + ' length ' + allTicks.length);
for (i = 0 ; i < allTicks.length; i++ ){
$myTable.find('#' + allTicks[i]).prop('checked', true);
}
msg = 'After: '
$myTable.find('input').each(function(){
msg = msg + this.id + " " + $(this).prop('checked') + '\n';
});
//alert(msg);
});
}, $refreshInterval);
If I uncomment the alert lines, and check 2 checkboxes, on the next update I get (for 3 row table):
Before: host2 false
host3 false
host4 false
object [Object] length 2
After: host2 false
host3 false
host4 false
Also did a previous check on the contents of the array and it has all the correct entries.
Can the DOM change or working with an entirely new table instance be a cause of this?
EDIT2:
Here's a sample of the table generated by the JSP (edited for confidentiality purposes):
<table id="my-table" class="tablesorter">
<thead>
<tr>
<th>Full Name</th>
<th>IP Address</th>
<th>Role</th>
<th>Job Slots</th>
<th>Status</th>
<th>Management</th>
</tr>
</thead>
<tbody>
<tr>
<td>head</td>
<td>10.20.1.14</td>
<td>H</td>
<td>4</td>
<td>ON</td>
<td>Permanent</td>
</tr>
<tr>
<td>
<input type="checkbox" id="host2" name="host2"/>
host2
</td>
<td>10.20.1.7</td>
<td>C</td>
<td>4</td>
<td>BSTART</td>
<td>Dynamic</td>
</tr>
<tr>
<td><input type="checkbox" id="host3" name="host3"/>
host3</td>
<td>10.20.1.9</td>
<td>C</td>
<td>4</td>
<td>BSTART</td>
<td>Dynamic</td>
</tr>
<tr>
<td><input type="checkbox" id="host4" name="host4"/>
host4</td>
<td>10.20.1.11</td>
<td>C</td>
<td>4</td>
<td>BSTART</td>
<td>Dynamic</td>
</tr>
</tbody>
</table>
Note that the id and name of the checkbox coincide with the host name. Also note that the first td does not have a checkbox. That's the expected behavior.
Changing 'special' attributes like disbaled or checked should be done like this:
$(...).attr('checked','checked');
or this way if you are using jQuery 1.6 or later:
$(...).prop('checked', true); // more reliable
See jQUery doc about .attr() and .prop()
Here's your piece of code modified with a few optimizations (check the comments):
refreshId = setInterval(function()
{
var allTicks = [],
$myTable = $('#myTable'); // select once and re-use
// .map() returns an array which is what you are after
// also never do this: $(this).attr('id').
// 'id' is a property available in javascript and
// in .map() (and in .each()), 'this' is the current DOMElement so simply do:
// this.id
allTicks = $myTable.find('input:checked').map(function() { return this.id; });
$myTable.load('/get-table.jsp', null, function (responseText,textStatus, req ) {
$myTable.tablesorter();
//alert(allTicks + ' length ' + allTicks.length);
for (i = 0 ; i < allTicks.length; i++ )
// avoid prefixing with tagname if you have the ID: input#theId
// #xxx is unique and jquery will use javascript getElementById which is super fast ;-)
$myTable.find('#' + allTicks[i]).prop('checked', true);
});
}, $refreshInterval);
Let us assume that the JavaScript does retrieve and set the checkboxes ticks.
Then there still is a problem with the asynchrone Ajax call.
First try it with a very large $refreshInterval.
Place the for-loop before the tablesorter call.
Do not setInterval, but setTimeout and schedule this for one single time.
Then in the load function schedule the next time.
This prevents overlapping calls which were a possible cause for the error.
But may stop refreshing, when the load is not called. (Not so important.)
After lots of painful hours of digging up every small detail, I realized that my problem was not how I coded the thing, nor was it stuff like unexpected DOM changes, but a simple detail I failed to see:
The id I was trying to assign to the checkbox contained a period (".") character.
This causes lots of problems for jQuery when trying to look up that sort of id, because a period as-is acts as a class descriptor. To avoid this, the period character must be escaped using 2 backslashes.
For example:
$("#my.id") // incorrect
$("#my\\.id") // correct
So then the fix in my case would be:
$myTable.find('#' + allTicks[i].replace(".", "\\.")).prop('checked', true);
... and it finally works.
Thanks everyone for all your helping hands!

Categories