How could I show a total of a column on datatables? I tried with the datatables doc example but I don't understand how does it works.
I tried to adapt the datatables doc example with like this:
jQuery('#tableName').DataTable({
pagingType: 'full',
lengthMenu: [
[5, 10, 25, 50, -1],
[5, 10, 25, 50, 'All'],
],
searching: true,
ordering: true,
aaSorting: [],
paging: true,
select: false,
info: true,
responsive: true,
data: [
{ col1: "col1.1", col2: 20 },
{ col1: "col1.2", col2: 30 }
],
columns: [
{ data: 'col1', title: 'col1' },
{ data: 'col2', title: 'col2' }
],
dom: 'lBfrtip',
buttons: [{
extend: 'copy',
text: '<i class="far fa-copy"></i>',
title: lang.componentSection,
footer: true
}, {
extend: 'pdf',
text: '<i class="far fa-file-pdf"></i>',
title: lang.componentSection,
footer: true
}, {
extend: 'excel',
text: '<i class="far fa-file-excel"></i>',
title: lang.componentSection,
footer: true
}, {
extend: 'print',
text: '<i class="fas fa-print"></i>',
title: lang.componentSection,
footer: true
}],
footerCallback: function ( row, data, start, end, display ) {
var api = this.api(), data;
// Remove the formatting to get integer data for summation
var intVal = function ( i ) {
return typeof i === 'string' ?
i.replace(/[\$,]/g, '')*1 :
typeof i === 'number' ?
i : 0;
};
// Total over all pages
var total = api
.column( 1 )
.data()
.reduce( function (a, b) {
return intVal(a) + intVal(b);
}, 0 );
// Total over this page
var pageTotal = api
.column( 1, { page: 'current'} )
.data()
.reduce( function (a, b) {
return intVal(a) + intVal(b);
}, 0 );
// Update footer
$( api.column( 1 ).footer() ).html(
'$'+pageTotal +' ( $'+ total +' total)'
);
}
});
but I don't know how to show the results or if there's something bad. (I have no errors in console and this is a reduced example, in real datatable I get the data from ajax)
I have the total value, but I don't know how can be shown with this sintax (column defs)
I can show the total addinf the html tags with jQuery, but with this method total will not exported to pdf, excel, ...
here is the example code:
jQuery('#tableName').append('<tfoot>\
<tr>\
<th colspan="4" style="text-align:right">Total:' + pageTotal + '</th>\
<th></th>\
</tr>\
</tfoot>');
The only way to esport the footer is to have it painted before the datatable generation like in this simplified example:
jQuery('#' + tableName).html('<tfoot>\
<th></th>\
<th style="text-align:center; border:0.5px solid gray"></th>\
</tfoot>');
jQuery('#tableName').DataTable({
pagingType: 'full',
lengthMenu: [
[5, 10, 25, 50, -1],
[5, 10, 25, 50, 'All'],
],
searching: true,
ordering: true,
aaSorting: [],
paging: true,
select: false,
info: true,
responsive: true,
data: [
{ col1: "col1.1", col2: 20 },
{ col1: "col1.2", col2: 30 }
],
columns: [
{ data: 'col1', title: 'col1' },
{ data: 'col2', title: 'col2' }
],
dom: 'lBfrtip',
buttons: [{
extend: 'copy',
text: '<i class="far fa-copy"></i>',
title: lang.componentSection,
footer: true
}, {
extend: 'pdf',
text: '<i class="far fa-file-pdf"></i>',
title: lang.componentSection,
footer: true
}, {
extend: 'excel',
text: '<i class="far fa-file-excel"></i>',
title: lang.componentSection,
footer: true
}, {
extend: 'print',
text: '<i class="fas fa-print"></i>',
title: lang.componentSection,
footer: true
}],
footerCallback: function ( row, data, start, end, display ) {
var api = this.api(), data;
var intVal = function ( i ) {
return typeof i === 'string' ?
i.replace(/[\$,]/g, '.')*1 :
typeof i === 'number' ?
i : 0;
};
var total = api.column( 1 ).data()
.reduce( function (a, b) {
return intVal(a) + intVal(b);
}, 0 );
total = total.toString().replace(/[\$.]/g, ',');
jQuery(api.column( 1 ).footer()).html(total);
}
});
is important to put all of the <th></th>, because collspan doesn't exports correctly on datatables
the idea is to inject the footer with jQuery so you could destroy the table and regenerate again with the same footer (for example if you have to put that table with new columns or someting like that)
It doesn't works without api.column( 1 ).data() because if you put it with jQuery can't be exportable.
Related
I am using DataTables and would like to set the background colour for each row depending on the input. I followed the directions in the forum:
https://datatables.net/forums/discussion/36595/change-the-row-color-based-on-column-data
[https://datatables.net/forums/discussion/62460/changing-row-color-at-rendering-time-based-on-column-values]
However, I can not get it to work.
My code is:
createdRow: function( row, data, dataIndex){
//console.log('data[3]: ' + data[3]);
if( data[3] == '4'){
$(row).css("background-color", "red");
}
},
The console.log displays "data[3] is undefined".
I have tried:
if( data[3] === '4')
The full context is:
//Show DataTable
moment.updateLocale(moment.locale(), { invalidDate: "" })
if ( $.fn.dataTable.isDataTable( '#ymTable' ) ) {
var ymTable = $('#ymTable').DataTable();
}
else {
var ymTable = $('#ymTable').DataTable( {
createdRow: function( row, data, dataIndex){
// console.log('data[3]: ' + data[3]);
if( data[3] == '4'){
$(row).css("background-color", "red");
}
},
info: false,
dom: 'Bfrtip',
order: [[ 3, 'asc' ], [ 1, 'asc' ], [ 2, 'asc' ]],
// buttons: ['copy', 'csv', 'excel', 'pdf', 'print'],
buttons: [
{
extend: 'copy',
exportOptions: {
columns: [ 1, 2, 3, 4, 5, 6 ]
}
},
{
extend: 'csv',
exportOptions: {
columns: [ 1, 2, 3, 4, 5, 6 ]
}
},
{
extend: 'excel',
exportOptions: {
columns: [ 1, 2, 3, 4, 5, 6 ]
}
},
{
extend: 'pdf',
exportOptions: {
columns: [ 1, 2, 3, 4, 5, 6 ]
}
},
{
extend: 'print',
exportOptions: {
columns: [ 1, 2, 3, 4, 5, 6 ]
}
},
],
columns: [
{data: 'cdId',
visible: false,
searchable: false},
{data: 'surname',
defaultContent: ""},
{data: 'firstname',
defaultContent: ""},
{data: 'age',
defaultContent: ""},
{data: 'gender',
defaultContent: ""},
{data: 'paradePatrol',
defaultContent: ""},
{data: 'role',
defaultContent: ""},
{data: null,
className: "center",
render: function(data,type,row) {
if(data.sayId == null || data.sayId == undefined){
return ("<input type='checkbox' id=" + data.cdId + " name='update' onchange='ymActivityPatrolFunction(this)' style='zoom: 2.0;'>")
}else{
return ("<input type='checkbox' id=" + data.cdId + " name='update' onchange='ymActivityPatrolFunction(" + data.cdId + ", " + this.checked + ")' style='zoom: 2.0;' checked>");
}
},
},
],
columnDefs: [
{targets: 7, orderable: false},
],
});
}
I found the answer to be, to replace:
data[3] == '4'
with:
data.age == '4'
As in:
createdRow: function( row, data, dataIndex){
if( data.age == '4'){
$(row).css("background-color", "red");
}
},
I'm trying to enable some operations on my grid such as grouping, filtering and sorting, individually they works as shown in the docs but there is no an example of those functionality working together.
By myself I was able to combine sorting and filtering but grouping does not work when i'm adding it as it shown in the docs. look at at my code
<template>
<div>
<Grid :style="{height: '100%'}"
ref="grid"
:data-items="getData"
:resizable="true"
:reorderable="true"
#columnreorder="columnReorder"
:filterable="true"
:filter="filter"
#filterchange="filterChange"
:sortable="true"
:sort= "sort"
#sortchange="sortChangeHandler"
:groupable="true"
:group= "group"
#dataStateChange="dataStateChange"
:columns="columns">
</Grid>
</div>
</template>
<script>
export default {
data() {
return {
items: [],
editID: null,
columns: [
{ field: 'AbsenceEmployeID', filterable:false, editable: false, title: '#'},
{ field: 'Employe', title: 'Employer', cell: DropDownEmployes},
{ field: 'Remarque', title: 'Remarque'},
{ field: 'Type', title: 'Type', cell: DropDownTypes},
{ field: 'CreatedDate', filter:'date', editable: false, editor: 'date', title: 'créé le', format: '{0:d}'},
{ title: 'Actions', filterable:false, cell: CommandCell}
],
filter: {
logic: "and",
filters: []
},
sort: [
{ field: 'CreatedDate', dir: 'desc' }
],
group: [],
gridData: []
}
}
mounted() {
this.loadItems()
},
computed: {
absencesList() {
return this.items.map((item) => Object.assign({ inEdit: item.AbsenceEmployeID === this.editID}, item));
},
getData() {
return orderBy(filterBy(this.absencesList, this.filter), this.sort);
},
...mapState({
absences: state => state.absences.absences
})
}
methods: {
loadItems () {
this.$store.dispatch('absences/getAbsences')
.then(resp => {
this.items = this.absences.map(item => item)
})
},
filterChange: function(ev) {
this.filter = ev.filter;
},
columnReorder: function(options) {
this.columns = options.columns;
},
sortChangeHandler: function(e) {
this.sort = e.sort;
},
// the following is for grouping but not yet used, read more
groupedData: function () {
this.gridData = process(this.getData, {group: this.group});
},
createAppState: function(dataState) {
this.group = dataState.group;
this.groupedData();
},
dataStateChange: function (event) {
this.createAppState(event.data);
},
}
}
</script>
The last three methods are not used yet, so filtering and sorting is working perfectly as of now. then in other to enable grouping I want to replace :data-items="getData" by :data-items="gridData" and run this.groupedData() method after the items are loaded but grouping doesn't work.
I think everything should be handle by the dataStateChange event and process() function but I also tried but without success
If you define the filterchange and sortchange events they are being triggered for filter and sort and you will have to updated data in their handlers. If you rather want to use datastatechage event for all the changes you have to remove the filterchange and sortchange events and the datastatechage event will be triggered instead of them. In this case you will have to update the data in its handler.
You can use the process method of #progress/kendo-data-query by passing the respective parameter each data change that is needed as in the example below:
const result = process(data, {
skip: 10,
take: 20,
group: [{
field: 'category.categoryName',
aggregates: [
{ aggregate: "sum", field: "unitPrice" },
{ aggregate: "sum", field: "unitsInStock" }
]
}],
sort: [{ field: 'productName', dir: 'desc' }],
filter: {
logic: "or",
filters: [
{ field: "discontinued", operator: "eq", value: true },
{ field: "unitPrice", operator: "lt", value: 22 }
]
}
});
Hers is a sample stackblitz example where such example is working correctly - https://stackblitz.com/edit/3ssy1k?file=index.html
You need to implement the groupchange method to handle Grouping
I prefer to use process from #progress/kendo-data-query
The following is a complete example of this
<template>
<Grid :style="{height: height}"
:data-items="gridData"
:skip="skip"
:take="take"
:total="total"
:pageable="pageable"
:page-size="pageSize"
:filterable="true"
:filter="filter"
:groupable="true"
:group="group"
:sortable="true"
:sort="sort"
:columns="columns"
#sortchange="sortChangeHandler"
#pagechange="pageChangeHandler"
#filterchange="filterChangeHandler"
#groupchange="groupChangeHandler"
/>
</template>
<script>
import '#progress/kendo-theme-default/dist/all.css';
import { Grid } from '#progress/kendo-vue-grid';
import { process } from '#progress/kendo-data-query';
const sampleProducts = [
{
'ProductID': 1,
'ProductName': 'Chai',
'UnitPrice': 18,
'Discontinued': false,
},
{
'ProductID': 2,
'ProductName': 'Chang',
'UnitPrice': 19,
'Discontinued': false,
},
{
'ProductID': 3,
'ProductName': 'Aniseed Syrup',
'UnitPrice': 10,
'Discontinued': false,
},
{
'ProductID': 4,
'ProductName': "Chef Anton's Cajun Seasoning",
'UnitPrice': 22,
'Discontinued': false,
},
];
export default {
components: {
Grid,
},
data () {
return {
gridData: sampleProducts,
filter: {
logic: 'and',
filters: [],
},
skip: 0,
take: 10,
pageSize: 5,
pageable: {
buttonCount: 5,
info: true,
type: 'numeric',
pageSizes: true,
previousNext: true,
},
sort: [],
group: [],
columns: [
{ field: 'ProductID', filterable: false, title: 'Product ID', width: '130px' },
{ field: 'ProductName', title: 'Product Name' },
{ field: 'UnitPrice', filter: 'numeric', title: 'Unit Price' },
{ field: 'Discontinued', filter: 'boolean', title: 'Discontinued' },
],
};
},
computed: {
total () {
return this.gridData ? this.gridData.length : 0;
},
},
mounted () {
this.getData();
},
methods: {
getData: function () {
this.gridData = process(sampleProducts,
{
skip: this.skip,
take: this.take,
group: this.group,
sort: this.sort,
filter: this.filter,
});
},
// ------------------Sorting------------------
sortChangeHandler: function (event) {
this.sort = event.sort;
this.getData();
},
// ------------------Paging------------------
pageChangeHandler: function (event) {
this.skip = event.page.skip;
this.take = event.page.take;
this.getData();
},
// ------------------Filter------------------
filterChangeHandler: function (event) {
this.filter = event.filter;
this.getData();
},
// ------------------Grouping------------------
groupChangeHandler: function (event) {
this.group = event.group;
this.getData();
},
},
};
</script>
I have created a datatable with following code:
userTable = $('#userTable').DataTable({
serverSide: true,
processing: true,
ajax: {
url: "{!! route('listOfUsersAjax') !!}",
type: "GET",
dataSrc: function ( json ) {
//console.log(json);;
for ( var i=0, ien=json.data.length ; i<ien ; i++ ) {
if (json.data[i].is_manager == 1){
json.data[i].is_manager = 'Yes';
}
else {
json.data[i].is_manager = 'No';
}
}
return json.data;
}
},
columns: [
{
className: 'details-control',
orderable: false,
searchable: false,
data: null,
defaultContent: ''
},
{ name: 'id', data: 'id' },
{ name: 'name', data: 'name' },
{ name: 'email', data: 'email' },
{ name: 'is_manager', data: 'is_manager'},
{ name: 'region', data: 'region' },
{ name: 'country', data: 'country' },
{ name: 'domain', data: 'domain' },
{ name: 'management_code', data: 'management_code' },
{ name: 'job_role', data: 'job_role' },
{ name: 'employee_type', data: 'employee_type' },
{
name: 'actions',
data: null,
sortable: false,
searchable: false,
render: function (data) {
var actions = '';
actions += '<div class="btn-group btn-group-xs">';
actions += '<button data-toggle="tooltip" title="view" id="'+data.id+'" class="buttonView btn btn-success"><span class="glyphicon glyphicon-eye-open"></span></button>';
actions += '<button data-toggle="tooltip" title="edit" id="'+data.id+'" class="buttonUpdate btn btn-primary"><span class="glyphicon glyphicon-pencil"></span></button>';
actions += '<button data-toggle="tooltip" title="delete" id="'+data.id+'" class="buttonDelete btn btn-danger"><span class="glyphicon glyphicon-trash"></span></button>';
actions += '</div>';
return actions;
}
}
],
columnDefs: [
{
"targets": [1,3,4], "visible": false, "searchable": false
}
],
order: [[2, 'asc']],
initComplete: function () {
this.api().columns().every(function () {
var column = this;
//console.log(userTable);
// Now we need to skip the first column as it is used for the drawer...
if(column[0][0] == '0' || column[0][0] == '11'){return true;};
var input = document.createElement("input");
$(input).appendTo($(column.footer()).empty())
.on('keyup change', function () {
column.search($(this).val(), false, false, true).draw();
});
});
}
} );
At the end, you can see that I have put a initComplete to have search columns to be at the bottom of each column.
I don't need to have a search when the column is not searchable, for example, first column and last one because it is not searchable. I am using the number of the column and returning true so that it doesn't create it but I would like something more dynamic and have an if column searchable is false then return true that way I don't need to specify the number of the column.
Thanks for your help.
You do have the columns definition available through this.api().init().columns. So all you have to do is to evaluate if a columns searchable explicit is set to false (not defining searchable or not defining the column at all means true, since this is the default) :
initComplete: function() {
var columns = this.api().init().columns;
this.api().columns().every(function(index) {
if (!columns[index] || columns[index].searchable) {
// column is searchable
} else {
// column is not searchable
}
})
}
How to add auto serial number column to jqxgrid when database records are in random order and
there is no specific field in database for serial number of records or
in case we are only dealing with a subset of records from a table?
Add this in your column describing area
{
text: 'Srl No', sortable: false, filterable: false, editable: false,
groupable: false, draggable: false, resizable: false,
datafield: '', columntype: 'number', width: 50,
cellsrenderer: function (row, column, value) {
return "<div style='margin:4px;'>" + (value + 1) + "</div>";
}
},
Then your Grid code look like below
<script type="text/javascript">
$(document).ready(function () {
// prepare the data
var data = MyJsonData;
var source =
{
localdata: data,
datatype: "json",
datafields:
[
{ name: 'firstname', type: 'string' },
{ name: 'lastname', type: 'string' }
]
};
var dataAdapter = new $.jqx.dataAdapter(source);
$("#jqxgrid").jqxGrid(
{
width: 300,
source: dataAdapter,
columnsresize: true,
columns: [
{
text: 'Srl No', sortable: false, filterable: false, editable: false,
groupable: false, draggable: false, resizable: false,
datafield: '', columntype: 'number', width: 50,
cellsrenderer: function (row, column, value) {
return "<div style='margin:4px;'>" + (value + 1) + "</div>";
}
},
{ text: 'Name', datafield: 'firstname', width: 125 },
{ text: 'Last Name', datafield: 'lastname', width: 125 }
]
});
});
</script>
Grid Cells Rendering alongwith following code will help
{ text: 'Serial Number', dataField: '', columntype: 'number', width: 50,cellsrenderer: slrenderer },
In order to render we
var slrenderer = function (value) {
value=value+1; // default numbering starts at 0
return '<div style="text-align: center; margin-top: 5px;">' + value + '</div>';
}
I am trying to connect a RallyChart using the type specifier set to pie with a Rally.data.custom.Store. When the RallyChart has the type set to column or is blank, the data shows correctly. When the type is set to pie, I get a pie with all 0%s returned.
Here's what my store looks like:
var myStore = Ext.create('Rally.data.custom.Store', {
data: mySeries,
fields: [
{ name: 'WorkItems', type: 'string' },
{ name: 'Count', type: 'int' }
]
});
Here's what my chart configuration function looks like:
_buildChart: function(myStore) {
this.myChart = Ext.create('Rally.ui.chart.Chart', {
height: 400,
store: myStore,
series: [{
type: 'pie',
name: 'Count',
dataIndex: 'Count'
}],
xField: 'WorkItems',
chartConfig: {
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
type: 'pie'
},
title: {
text: 'Work Breakdown in Selected Sprint'
},
tooltip: {
pointFormat: '{series.name}: <b>{point.percentage}%</b>',
percentageDecimals: 1
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: true,
color: '#000000',
connectorColor: '#000000',
formatter: function() {
return '<b>'+ this.point.name +'</b>: '+ this.percentage +' %';
}
}
}
}
}
});
this.add(this.myChart);
}
My data incoming looks like:
['Defects', 4],
['Feature A', 4]
['Feature B', 4]
Any ideas why column charts can show it, but pie cannot?
I bet this is a bug in the 2.0p5 version of the chart. We just released version 2.0rc1 of the SDK which includes a better version of the chart. The following code example shows how to create a pie with your data and Rally.ui.chart.Chart in 2.0rc1:
//from inside your app
this.add({
xtype: 'rallychart',
height: 400,
chartData: {
series: [{
type: 'pie',
name: 'Browser share',
data: [
['Defects', 4], ['Feature A', 4], ['Feature B', 4]
]
}]
},
chartConfig: {
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
type: 'pie'
},
xAxis: {},//must specify empty x-axis due to bug
title: {
text: 'Work Breakdown in Selected Sprint'
},
tooltip: {
pointFormat: '{series.name}: <b>{point.percentage}%</b>',
percentageDecimals: 1
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: true,
color: '#000000',
connectorColor: '#000000',
formatter: function() {
return '<b>'+ this.point.name +'</b>: '+ this.percentage +' %';
}
}
}
}
}
});
Note it is no longer necessary to create an intermediate store to pass to the chart- you can simply pass in your series as part of the chartData config.