datatables sorting data with mix numbers and letters - javascript

Updated with jsfiddle: https://jsfiddle.net/pnnorhtg/
I have a data table and I am having a difficult time initializing the 'numbers with html' data tables plug-in https://datatables.net/plug-ins/sorting/num-html.
It's initially sorted on 'Count' DESC. However once I execute my function that modifies and appends html to each of the cell in that column it is no longer sortable.
Based on my research this plug-in should be able to fix this but I am having no luck.
This is my data:
var preHtmlData = [{
Brand: "Toyota",
Count: 33423,
GBV: 242445
}, {
Brand: "Ford",
Count: 23558,
GBV: 334343
}, {
Brand: "Honda",
Count: 9466,
GBV: 933455
}];
This is my function that goes through and adds html text to the value based on the key:
//adding text next to Count
function updateItemCount(preHtmlData) {
for(var key in preHtmlData) {
var value = preHtmlData[key];
console.log(value)
if (value.Brand == 'Toyota') {
value.Count = value.Count + ' <div style="font-size: 10px;margin-top: -5px">Toyota Purchases</div>';
} else if (value.Brand == 'Ford') {
value.Count = value.Count + ' <div style="font-size: 10px;margin-top: -5px">Ford Purchases</div>';
} else if (value.LOB == 'Honda') {
value.Count = value.Count + ' <div style="font-size: 10px;margin-top: -5px">Honda Purchases</div>';
}
}
}
This is what where I am initializing table:
summary_data_table = $('#resultsTable').DataTable({
"bSort": true,
"destory": true,
"data": data,
"searching": false,
"paging": false,
"order": [
[aryJSONColTable.length - 1, "desc"]
],
"dom": '<"top">t<"bottom"><"clear">',
"columnDefs": aryJSONColTable,
[
{ type: 'natural-nohtml', targets: 5 }
]
"initComplete": function(settings, json) {
$("#resultsTable").show();
}
});
I've added the plug-in and constructed my code as per the documentation, I have a feeling its some how I've defined my columnDefs, but I need it to execute aryJSONColTable and the natural sorting.

UPDATE
As seen from your fiddle, you can overcome this problem by placing an order type("type":"natural") inside your custom properties that you pass on to columnDefs
customParams = {
"targets": keys.length - 1,
"sTitle": "Item Count",
"type":"natural"
}
Please see the updated fiddle for the solution https://jsfiddle.net/pnnorhtg/1/

Related

Populate a datatable starting from the second column

I have a datatable that retrieves json data from an api. The first column of the table should only contain a checkbox. However, when retrieving the data it populates the first column as well.
$.getJSON('https://api.myjson.com/bins/o44x', function(json) {
$('#parametrictable').DataTable({
data : json.data,
columns : json.columns,
columnDefs: [ {
orderable: false,
className: 'select-checkbox',
targets: 0
} ],
select: {
style: 'os',
selector: 'td:first-child'
},
order: [[ 1, 'asc' ]]
})
});
Is there any way that I can set that the data should only populate starting from the second column, leaving the first column to only contain the checkbox?
You can fix this issue by updating the api or in your js code just updating the data key value to null for first object in the json.columns array like:
$.getJSON('https://api.myjson.com/bins/o44x', function(json) {
json.columns[0].data = null;
json.columns[0].defaultContent = '';
$('#parametrictable').DataTable({
data: json.data,
columns: json.columns,
.... your rest of code
})
});
EDIT:
I meant what your suggestion only did was hide the data that was overlapping with the checkbox. I don't want it hidden. I want the first column to be, by default, only checkboxes. and the overlapping data should be on the 2nd column.
You can update your API like this to achieve that:
"columns": [
{
"data": null,
"defaultContent": ""
},
{
"data": "DT_RowId",
"title": "Id"
},
{
"data": "supplier",
"title": "supplier"
},
{
"data": "color",
"title": "color"
}
],
Or, you can update your code without modifying your API like:
$.getJSON('https://api.myjson.com/bins/o44x', function(json) {
// Add object for checkbox at first position
json.columns.unshift({"data": null, "defaultContent": ""});
$('#parametrictable').DataTable({
data: json.data,
columns: json.columns,
....your rest of code
})
});

Datatables, how to sort a table so a specific row is always the first row?

I got a small question.
My datatable uses AJAX to gather the rows for my table, the table sort on 6th. column.
Is it possible to always have a specific row to be first? No matter how the sorting is gone?
It does not matter if the row change position after the initial sorting.
My code looks like this:
$('#servertable').DataTable( {
"ajax": "/objects.php",
"deferRender": true,
"order": [[ 5, "desc" ]],
"pageLength": 25,
"columns": [
{ "data": "id" },
{
"data": "hostname",
"fnCreatedCell": function (nTd, sData, oData, iRow, iCol) {
$(nTd).html("<a href='/"+oData.url+"'>"+oData.hostname+"</a>");
}
},
{ "data": "version" },
{ "data": "country_cn" },
{ "data": "map_id" },
{ "data": "players" }
]
});
I think this is one of the cases, where a custom sorting plugin is in its place. I guess you want to have certain players on top of the list, all the time? If you have an array of player names which should stay on the top :
var players = ['Yuri Berry', 'Vivian Harrell'];
then you can implement a sorting plugin like this :
jQuery.extend( jQuery.fn.dataTableExt.oSort, {
"players-on-top-asc": function( a, b ) {
a = ~players.indexOf(a) ? new Array(255).join('a') : a;
return a.localeCompare(b);
},
"players-on-top-desc": function( a, b ) {
a = ~players.indexOf(a) ? new Array(255).join('z') : a;
return b.localeCompare(a);
}
});
usage :
$('#servertable').DataTable( {
...
"columns": [
...
{ "data": "players", type: "players-on-top" }
]
});
see a small demo, look for column #2 -> http://jsfiddle.net/ryfce85u/

Slickgrid 3 grouping how to remove totals row?

I use multi-row grouping and put the totals in the grouping headers.
I'm not using the totals in the totals rows. I see the the rows are grouped and where the totals would be there are empty rows. In my case there is an empty row after each of the child grouping and at the end there is an empty row for the parent totals.
How do I remove these totals rows?
Thank you!
.cshtml:
<div id="gridList" class="grid" style="width: 100%; height: 500px"></div>
.js
$(function() {
var columns = [
{
id: "isExcluded",
name: "Exclude",
field: "isExcluded" /*, width: 120*/,
formatter: Slick.Formatters.Checkmark,
editor: Slick.Editors.Checkbox, sortable: true
},
{
id: "symbol",
name: "Symbol",
field: "symbol",
sortable: true /*, width: 120*/
},
{
id: "price",
name: "Price",
field: "price",
sortable: true
//, groupTotalsFormatter: sumTotalsFormatter
},
{
id: "parent_name",
name: "Parent Name",
field: "parent_name",
sortable: true /*, width: 120*/
},
{
id: "description",
name: "Description",
field: "description",
sortable: true,
width: 120,
editor: Slick.Editors.Text,
},
{ id: "cancel_ind",
name: "Canceled",
field: "cancel_ind",
sortable: true, width: 80 }
];
function requiredFieldValidator(value) {
if (value == null || value == undefined || !value.length) {
return { valid: false, msg: "This is a required field" };
} else {
return { valid: true, msg: null };
}
};
var options = {
editable: true,
enableAddRow: true,
enableCellNavigation: true,
asyncEditorLoading: false,
autoEdit: true,
enableExpandCollapse: true,
rowHeight: 25
};
var sortcol = "parent_name";
var sortdir = 1;
var grid;
var data = [];
var groupItemMetadataProviderTrades = new Slick.Data.GroupItemMetadataProvider();
var dataView = new Slick.Data.DataView({ groupItemMetadataProvider: groupItemMetadataProviderTrades });
dataView.onRowCountChanged.subscribe(function (e, args) {
grid.updateRowCount();
grid.render();
});
dataView.onRowsChanged.subscribe(function (e, args) {
grid.invalidateRows(args.rows);
grid.render();
});
function groupByParentAndSymbol() {
dataViewTrades.setGrouping([
{
getter: "parent_name",
formatter: function(g) {
return "Parent: " + g.value + " <span style='color:green'>(" + g.count + " items) Total: " + g.totals.sum.price + "</span>";
},
aggregators: [
new Slick.Data.Aggregators.Sum("price")
],
aggregateCollapsed: true
,lazyTotalsCalculation: true
},
{
getter: "symbol",
formatter: function(g) {
return "Symbol: " + g.value + " <span style='color:green'>(" + g.count + " items) Total: " + g.totals.sum.price + "</span>";
},
aggregators: [
new Slick.Data.Aggregators.Sum("price")
],
collapsed: true
,lazyTotalsCalculation: true
}]);
};
grid = new Slick.Grid("#gridList", dataView, columns, options);
grid.registerPlugin(groupItemMetadataProviderTrades);
grid.setSelectionModel(new Slick.RowSelectionModel());
..... /*sorting support etc*/
// use instead of the default formatter <--- removed not used.
function sumTotalsFormatter(totals, columnDef) {
var val = totals.sum && totals.sum[columnDef.field];
//if (val != null) {
// return "total: " + ((Math.round(parseFloat(val) * 100) / 100));
//}
return "";
}
// will be called on a button click (I didn't include the code as irrelevant)
var getDataList = function () {
$.getJSON('/Home/GetData/', function (json) {
data = json;
dataView.beginUpdate();
dataView.setItems(data);
groupByParentAndSymbol();
dataView.endUpdate();
dataView.syncGridSelection(grid, true);
});
};
getDataList();
});
Adding displayTOtalsRow: false to the dataview solved my problem - the total rows are not shown now.
var dataView = new Slick.Data.DataView({ groupItemMetadataProvider: groupItemMetadataProviderTrades, displayTotalsRow: false });
To answer simply to your question... Just remove the aggregators: [...], when I say remove you have 2 options, you can remove whatever is the array [...] or you could simply erase completely that object line (so removing completely the aggregators[...]).
Now if you want more explanation of how it works...Let's give you some definition so you'll understand better. An aggregate is a collection of items that are gathered together to form a total quantity. The total in question could be a sum of all the fields, an average or other type of aggregator you might define. So basically it's the action of regrouping by the column you chose and give the calculation result of the group. Alright now how does it work in the case of a SlickGrid? You need to define what kind of Aggregator you want to use (Avg, Sum, etc...) that goes inside your function groupByParentAndSymbol(), defining them will do the calculation BUT unless you attach it to the field, nothing will be displayed, so in the example you found it is very important to attach/bind this groupTotalsFormatter: sumTotalsFormatter to your columns definition, as for example:
...var columns = [{id: "cost", ...width: 90, groupTotalsFormatter: sumTotalsFormatter}, ...];
so let's recap... Once the Aggregator is define new Slick.Data.Aggregators.Sum("cost") it will do the calculation but unless it's bind to the field nothing will be displayed. As an extra option, the aggregateCollapsed (true/false) is to display the sub-total (avg, sum or whatever) inside or outside the group when you collapse.

custom formatter for grouptext in jqgrid

Is there a way I can use custom formatter to format the grouptext value in jqgrid.
Current Output for var grouping=new Array("Environment", "System", "IIR","Userlimit","Kernel Parameters");
Suppose I have these groups.
var grouping=new Array("3Environment", "0System", "4IIR","2Userlimit","1Kernel Parameters");
If I sort it in ascending order it should display System, Kernel, Userlimit, Environment, IIR i.e., using some kind of custom formatter remove 01234 from 1st character or similar to sort my groups in some already decided order.
jqGrid Code
$('#compareContent').empty();
$('<div id="compareParentDiv" width="100%">'+
'<table id="list2" cellspacing="0" cellpadding="0"></table>'+
'<div id="gridpager3"></div></div>')
.appendTo('#compareContent');
$("#compareParentDiv").hide();
var gridDiff = $("#list2");
gridDiff.jqGrid({
datastr: compareData,
datatype: "jsonstring",
colNames: ['KeyName', 'SubCategory', starheader, header1,'isEqual'],
colModel: [
{ name: 'elementName', index: 'elementName', key: true, width: 120 },
{ name: 'subCategory', index: 'subCategory', width: 1 },
{ name: 'firstValue', index: 'firstValue', width: 310, jsonmap:'attribute.0.firstValue' },
{ name: 'secondValue', index: 'secondValue', width: 310,jsonmap:'attribute.0.secondValue' },
{ name: 'isEqual', index: 'isEqual', width: 1,hidden:true}
],
pager: '#gridpager3',
rowNum:60,
scrollOffset:1,
//viewrecords: true,
jsonReader: {
repeatitems: false,
page: function(){return 1;},
root: "response"
},
//rownumbers: true,
height: '320',
autowidth:true,
grouping: true,
groupingView: {
groupField: ['subCategory'],
groupOrder: ['desc'],
groupDataSorted : false,
groupColumnShow: [false],
//groupCollapse: true,
groupText: ['<b>{0}</b>']
},
loadComplete: function() {
if (this.p.datatype !== 'local') {
setTimeout(function () {
gridDiff.trigger('reloadGrid');
}, 0);
} else {
$("#compareParentDiv").show();
}
var i, names=this.p.groupingView.sortnames[0], l = names.length;
data = this.p.data, rows = this.rows, item;
for (i=0;i<l;i++) {
if ($.inArray(names[i],grouping) >= 0) {
$(this).jqGrid('groupingToggle',this.id+"ghead_"+i);
$(rows.namedItem(this.id+"ghead_"+i)).find("span.ui-icon").click(function(){
var len = data.length, iRow;
for (iRow=0;iRow<len;iRow++) {
item = data[iRow];
if (item.isEqual) {
$(rows.namedItem(item._id_)).hide();
}
}
});
} else {
// hide the grouping row
$('#'+this.id+"ghead_"+i).hide();
}
//console.info($('#'+this.id+"ghead_"+i));
}
var i, names=this.p.groupingView.sortnames[0], l = names.length,
data = this.p.data, rows = this.rows, item;
l = data.length;
for (i=0;i<l;i++) {
item = data[i];
if (!item.isEqual) {
$(rows.namedItem(item._id_))
.css({
"background-color": "#FFE3EA",
"background-image": "none"
});
} else {
$(rows.namedItem(item._id_)).hide();
}
}
}
});
gridDiff.jqGrid('navGrid', '#gridpager3', { add: false, edit: false, del: false, search: false, refresh: false });
gridDiff.jqGrid('navButtonAdd',"#gridpager3",{caption:"Toggle",title:"Toggle Search Toolbar", buttonicon :'ui-icon-pin-s',
onClickButton:function(){
gridDiff[0].toggleToolbar();
}
});
gridDiff.jqGrid('navButtonAdd',"#gridpager3",{caption:"Clear",title:"Clear Search",buttonicon :'ui-icon-refresh',
onClickButton:function(){
gridDiff[0].clearToolbar();
}
});
gridDiff.jqGrid('filterToolbar',
{stringResult: true, searchOnEnter: false, defaultSearch: 'cn'});
Update
Or is there a way to use positioning to place each grouped item, if sorting is not an option
Updated after accepting answer by Oleg
Page1
Page3
It seems to me that you have not the problem with the custom formatter of the grouptext
You use groupingView having groupDataSorted: false so the sorting of the group (in your case groupField: ['subCategory'] with groupOrder: ['asc']) do jqGrid for you. So the standard sorting behavior will be used.
jqGrid supports sorttype property of the colModel which define how the column should be sorted. If you need custom sorting order you should define sorttype property as a function which returns integer or string used instead of the cell value from the column. the prototype of the sorttype function could be function(cellValue,rowData) so it is possible to define the order which not only the cell value of the sorted column will be used but the whole contain of the row inclusive _id_ property. In you case the usage of the first cellValue parameter would be enough.
So to solve you problem you can for example define an array with the order of 'subCategory' values which you need:
var orderOfSubCategory = [
"System", "system", "Kernel", "Kernel Parameters", "Userlimit",
"Environment", "IIR", "Product"
];
and then define 'subCategory' column as following:
{
name: 'subCategory',
index: 'subCategory',
width: 1,
sorttype: function (value) {
return $.inArray(value, orderOfSubCategory);
}
}
You should don't forget, that jQuery.inArray return 0-based index of the item or -1 if the item will be not found in the array. So the values of 'subCategory' which could be not found in the orderOfSubCategory array will be placed the first. See here the corresponding demo.
Some other small remarks about your code. You use new Array(...) which is bad practice in JavaScript instead of that you should use always [...] construct which do the same is shorter and work quickly. Example: var grouping = ["Environment", "System", "IIR","Userlimit","Kernel Parameters"];
Another place which is difficult for to read for me is:
$('#compareContent').empty();
$('<div id="compareParentDiv" width="100%">'+
'<table id="list2" cellspacing="0" cellpadding="0"></table>'+
'<div id="gridpager3"></div></div>')
.appendTo('#compareContent');
$("#compareParentDiv").hide();
Here you first use cellspacing="0" cellpadding="0" attributes of the table which are unneeded and seconds use empty(), append() combination instead of one html() call. The following code do the same, but it seems me better:
$('#compareContent').html(
'<div id="compareParentDiv" style="display: none" width="100%">' +
'<table id="list2"></table><div id="gridpager3"></div></div>');

Using jQuery, how to check whether a table cell is empty or not?

Using jQuery, how to check whether a table cell is empty or not?
Please help me.
What do you mean by empty.
//cell maycontain new lines,spaces,&npsp;,... but no real text or other element
$("cellselector").text().trim()=="";
or
//cell has no child elements at all not even text e.g. <td></td>
$("cellselector:empty")
You can use CSS selectors with the $ function to get a reference to the cell's element, and then use the html function to see whether it has any contents. For instance, if the cell has an ID "foo":
if ($("#foo").html()) {
// The cell has stuff in it
}
else {
// The cell is empty
}
Try this:
$content = $('#your_cell_id_here').html();
if($content == '')
{
// yes it is empty
}
else
{
// no it is not empty
}
You can use the trechnique I posted here
http://www.keithrull.com/2010/06/09/HowToChangeTableCellColorDependingOnItsValueUsingJQuery.aspx
You can use this if you already know the id of the cell you want to check
$valueOfCell = $('#yourcellid').html();
or you can iterate on the cells of the table
$("#yourtablename tr:not(:first)").each(function() {
//get the value of the table cell located
//in the third column of the current row
var valueOfCell = $(this).find("td:nth-child(3)").html();
//check if its greater than zero
if (valueOfCell == ''){
//place action here
}
else{
//place action here
}
});
You could add css classes to your table and its rows and columns, then use jquery selectors to get the table item:
if ( !($('#mytable tr.row-i td.column-j').html()) ) { alert("empty"); }
for datatables, you may use the following
var missedClockoutDataTable = $("#missedClockoutsDatatable").DataTable({
"order": [[0, "asc"]],
"serverSide": true,
"processing": true,
responsive: true,
destroy: true,
select: true,
fixedHeader: {
header: true,
headerOffset: $('#fixed').height()
},
buttons: [
{extend: 'copy', className: 'ui button'},
{extend: 'csv', className: 'ui button'},
{extend: 'excel', className: 'ui button'},
{extend: 'pdf', className: 'ui button'},
],
dom:
"<'row'<'col-sm-3'l><'col-sm-6 text-center'B><'col-sm-3'f>>" +
"<'row'<'col-sm-12'tr>>" +
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
"ajax": {
"url": "{{url('workplace/dashboard/employees/missed-clockouts')}}",
data: function (d) {
d.gender = $("#gender").val();
d.roles = $("#roles").val();
},
},
"columns": [
{data: 'full_name', name: 'full_name'},
{data: 'clock', name: 'clock'},
{data: 'in', name: 'in'},
{data: 'out', name: 'out'},
{data: 'numberofHours', name: 'numberofHours'},
{data: 'clockoutReason', name: 'clockoutReason'},
{data: 'action', name: 'action'},
],
//when the table has fully loaded, proceed with looping through each row except the first one and then delete the rows if the IN column cell is empty
"initComplete": function(settings, json) {
// if the in type is empty, then hide that row
$("#missedClockoutsDatatable tr:not(:first)").each(function() {
//get the value of the table cell located
//in the third column of the current row
var valueOfCell = $(this).find("td:nth-child(3)").html();
//check if its greater than zero
if (valueOfCell == ''){
console.log(222);
this.remove();
}
});
}
});

Categories