DataTable - Serverside - sort columns initialisation - datatable

I have 6 datatables with one datatable configuration on a page with a basic layout as follows for all five tables:
<table>
<thead>
<tr>
<th>COL_1</th>
<th>COL_2</th>
<th>COL_1</th>
<th>COL_1</th>
<th>COL_1</th>
</tr>
</thead>
<tbody>
<tr><td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
and my datatable configuration options is as below:
{
order: [[5, "desc"], [2, "asc"]]
.......
}
My issue is I need to order by columns by table. For example, in table 1, I would like to order columns 1 and 4 for second table on columns 3 and 4 and third on columns 0 and 3 etc., Is there a way to do that without having six different configurations for each datatable?

You can parameterize the DataTable initialization code - to a certain extent.
As an example, assume the following three simple tables:
$('#example1').DataTable( {
"paging": true,
"order": [[5, desc"], [2, "asc"]]
} );
$('#example2').DataTable( {
"paging": true,
"order": [[2, "asc"], [3, "asc"]]
} );
$('#example3').DataTable( {
"paging": true,
"order": [[4, "asc"], [2, "desc"]]
} );
The only parts which differ from table to table are:
the table name
the default sort order
We can extract these differences into a JavaScript object and then iterate over it as follows:
var configs = [
{
tableName: 'example1',
order: [[5, "desc"], [2, "asc"]]
},
{
tableName: 'example2',
order: [[2, "asc"], [3, "asc"]]
},
{
tableName: 'example3',
order: [[4, "asc"], [2, "desc"]]
}
];
configs.forEach( function ( config ) {
console.log(config.tableName);
$('#' + config.tableName).DataTable( {
"paging": true,
"order": config.order
} );
} );
The end result is the same as the three separate table definitions.
You can, of course add more data to the config object, such as a source URL, and so on. There may be some configuration items which cannot be handled in this way - for example, nested functions. It depends on how complex you need your table initialization to be.
It's a judgement call as to whether this is worth doing, in your case. There is a loss of readability, which needs to be considered.

Related

How to sort Datatable by days in string value

I am currently using Datatable in my laravel project for displaying my day configuration from my database. I would like to display my data in the following order, Monday -> Tuesday -> Wednesday etc.
Currently it is being ordered by alphabetical order from my database where the day column is store as string. Below are my javascript codes for my table.
var ophrTables = $('#ophrs_table').DataTable({
stateSave: true,
columnDefs: [{
"searchable": false,
"orderable": false,
"targets": 0
},{
"searchable": true,
"orderable": true,
"targets": 1
},{
"searchable": false,
"orderable": false,
"targets": 2
},{
"searchable": false,
"orderable": false,
"targets": 3
}],
order: [[ 1, 'asc' ]]
});
Here are two approaches:
Use a Column Renderer
You can create a mapping from day names to numbers:
var days = { 'Monday': 1, 'Tuesday': 2, 'Wednesday': 3, 'Thursday': 4, 'Friday': 5, 'Saturday': 6, 'Sunday': 7 };
You can then use that mapping when you create your DataTable.
Here is my test data in my HTML table:
<table id="example" class="display dataTable cell-border" style="width:100%">
<thead>
<tr>
<th>Name</th>
<th>Day</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tiger Nixon</td>
<td>Monday</td>
</tr>
<tr>
<td>Garrett Winters</td>
<td>Tuesday</td>
</tr>
<tr>
<td>Ashton Cox</td>
<td>Wednesday</td>
</tr>
<tr>
<td>Cedric Kelly</td>
<td>Thursday</td>
</tr>
<tr>
<td>Airi Satou</td>
<td>Friday</td>
</tr>
<tr>
<td>Brielle Williamson</td>
<td>Saturday</td>
</tr>
<tr>
<td>Herrod Chandler</td>
<td>Sunday</td>
</tr>
</tbody>
</table>
Here is my DataTable definition:
$('#example').DataTable( {
"columnDefs": [ {
"targets": 1,
"render": function ( data, type, row, meta ) {
if (type === 'sort') {
return days[data];
} else {
return data;
}
}
} ]
} );
For the second column (index = 1), I use a render function to map from the name of the day to an integer. DataTables will use this integer for sorting operations (type === 'sort'). Otherwise it will use the name of the day.
The days[data] expression is used to look up the relevant number from my days variable.
The data looks like this when it is sorted by day name:
Warning:
When you use a renderer which produces numeric sort data from data which is alphanumeric, you do have to be careful. Even though numbers are used for sorting, they are treated as alphanumeric. In our case, this makes no difference, because the string values "1" through "7" are sorted the same way as the integer values 1 through 7.
But if you wanted to do something similar with the months of the year, then you would run into problems, as October (10), November (11) and December (12) would potentially be mis-sorted.
One fix for this is to force the column to be treated as if it contains numeric data by default: "type": "num",. Credit to this answer for highlighting this potential issue.
(Forcing the return value to be an integer does not help: return parseInt(days[data]);).
Delegated Sorting
An alternative approach is to populate the relevant number into an extra column when you load your data into the table.
In your DataTable definition, you can hide this column:
"columnDefs": [
{ "visible": false, "targets": 2 }
]
Then you can use the DataTables orderData option to delegate sorting from the visible "day name" column to the hidden "day number" column:
"columnDefs": [
{ "visible": false, "targets": 2 },
{ "orderData": [ 1 ], "targets": 2 }
]
This tells DataTables to use the data in column index 2 when you sort on column index 1.
I don't think that you can order by day. May be you can put a hidden text in the datatable column.
Now
<td>Monday</td>
<td>Tuesday</td>
<td>Friday</td>
Change it to
<td><span style="display:none">1</span>Monday</td>
<td><span style="display:none">2</span>Tuesday</td>
<td><span style="display:none">5</span>Friday</td>
Once you change it to this style, you can order by day.

Sort table Vue.js

I am trying to sort a table by columns. That when pressing the ID button all the column is ordered from highest to lowest or vice versa, and the same by pressing the other two. How can I do it?
<table id="mi-tabla">
<thead>
<tr class="encabezado-derecha" >
<th>ID</th>
<th>Nombre de sección</th>
<th>Potencial (€)</th>
</tr>
</thead>
<tbody>
<tr class="item" v-for="user in userInfo" #click="openDiv(), showInfo1(user.id_section)">
<td>{{user.id_section}}</td>
<td>{{user.desc_section}}</td>
<div class="acceder">
<td>{{user.sale_potential | currency}}</td>
<img src="../../iconos/icon/chevron/right#3x.svg" alt />
</div>
</tr>
</tbody>
</table>
{
"id_store": 4,
"id_section": 1,
"desc_section": "MATERIALES DE CONSTRUCCION yeh",
"id_rule": 1,
"sale_potential": "69413.5525190617"
},
{
"id_store": 4,
"id_section": 2,
"desc_section": "CARPINTERIA Y MADERA",
"id_rule": 1,
"sale_potential": "74704.3439572555"
},
{
"id_store": 4,
"id_section": 3,
"desc_section": "ELECTR-FONTAN-CALOR",
"id_rule": 1,
"sale_potential": "101255.89182774"
}
]
Here's what it might look like if you want to implement yourself, note that this is very basic functionality and as you start to add additional features, you might see more benefit from using a component that already does it all.
Anyhow, the way you can do it is by using a computed (sortedList) to store a sorted version of the array. Then use another data variable to store which column you want to store by (sortBy), and optionally, you can store a sort direction (sortOrder)
then add a sort method that passes the sort key and updates the sortBy value and/or the sortOrder. When either of these values (or even the source array) changes, the computed will re-sort the array using the sort function.
new Vue({
el: "#app",
data: {
sortBy: "id_section",
sortOrder: 1,
userInfo: [
{
"id_store": 4,
"id_section": 1,
"desc_section": "MATERIALES DE CONSTRUCCION yeh",
"id_rule": 1,
"sale_potential": "69413.5525190617"
},
{
"id_store": 4,
"id_section": 2,
"desc_section": "CARPINTERIA Y MADERA",
"id_rule": 1,
"sale_potential": "74704.3439572555"
},
{
"id_store": 4,
"id_section": 3,
"desc_section": "ELECTR-FONTAN-CALOR",
"id_rule": 1,
"sale_potential": "101255.89182774"
}
]
},
computed: {
sortedList() {
return [...this.userInfo]
.map(i => ({...i, sale_potential:parseFloat(i.sale_potential)}))
.sort((a,b) => {
if (a[this.sortBy] >= b[this.sortBy]) {
return this.sortOrder
}
return -this.sortOrder
})
}
},
methods: {
sort: function(sortBy){
if(this.sortBy === sortBy) {
this.sortOrder = -this.sortOrder;
} else {
this.sortBy = sortBy
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
[{{sortBy}}] [{{sortOrder}}]
<table id="mi-tabla">
<thead>
<tr class="encabezado-derecha">
<th #click='sort("id_section")'>{{ sortBy === 'id_section' ? '*' : '' }}ID</th>
<th #click='sort("desc_section")'>{{ sortBy === 'desc_section' ? '*' : '' }}Nombre de sección</th>
<th #click='sort("sale_potential")'>{{ sortBy === 'sale_potential' ? '*' : '' }}Potencial (€)</th>
</tr>
</thead>
<tbody>
<tr class="item" v-for="user in sortedList">
<td>{{user.id_section}}</td>
<td>{{user.desc_section}}</td>
<div class="acceder">
<td>{{user.sale_potential | currency}}</td>
<img src="../../iconos/icon/chevron/right#3x.svg" alt />
</div>
</tr>
</tbody>
</table>
</div>
I would recommend you to use bootstrap Vue tables which come with filtering and sorting. All you have to do is pass your data to the table.
Here is a link you can check it out.
https://bootstrap-vue.js.org/docs/components/table#complete-example
< script >
export default {
data() {
return {
items: [{
"id_store": 4,
"id_section": 1,
"desc_section": "MATERIALES DE CONSTRUCCION yeh",
"id_rule": 1,
"sale_potential": "69413.5525190617"
},
{
"id_store": 4,
"id_section": 2,
"desc_section": "CARPINTERIA Y MADERA",
"id_rule": 1,
"sale_potential": "74704.3439572555"
},
{
"id_store": 4,
"id_section": 3,
"desc_section": "ELECTR-FONTAN-CALOR",
"id_rule": 1,
"sale_potential": "101255.89182774"
}
],
fields: [{
key: 'id_store',
label: 'id',
sortable: true
}, {
key: 'desc_section',
label: 'Nombre de sección'
}, {
key: 'sale_potential'
},{key:'actions'}]
}
},
} <
/script>
<b-table striped hover :items="items" :fields="fields">
<template v-slot:cell(sale_potential)="row">
<p>{{row.item.sale_potential |currency}}</p>
<img src="../../iconos/icon/chevron/right#3x.svg" alt />
</template>
<template v-slot:cell(actions)="row">
<button #click="openDiv(); showInfo1(row.item.id_section);"
class="btn" variant="primary">Action</button>
</template>
</b-table>
If you want to add this functionality yourself you can achieve it using a computed value to sort your data.
data () => ({
...
sortBy : null,
}),
computed : {
userInfoSorted () {
const sortBy = this.sortBy
if (!sortBy) return this.userInfo
return this.userInfo.sort((a,b)=> a[sortBy] > b[sortBy] ? 1 : -1)
}
}
Then update your sortBy value within the <th> tags in your template:
<th #click="sortBy='id_section'">ID</th>
and link your rows to the computed value:
<tr class="item" v-for="user in userInfoSorted">
EDIT: CHANGE SORT ORDER
To add an option to toggle the order, start by adding the headers to your data object:
data () => ({
...
headers : {
id_section : {
text : 'ID',
reverse : true
}
}
})
Then update your template to also change the reverse value on click:
<th v-for="(val,key) in headers" #click="sortBy=key; val.reverse=!val.reverse">
{{ val.text }}
</th>
Finally include the reverse value in your sort function:
userInfoSorted () {
const sortBy = this.sortBy
const r = this.headers[sortBy].reverse ? -1 : 1;
if (!sortBy) return this.userInfo
return this.userInfo.sort((a,b)=> a[sortBy] > b[sortBy] ? 1*r : -1*r)
}

JQuery DataTables: Server-Side Search Function Breaking with SQL Error

I have a simple DataTable that I populate with data from a database, specifically from a single view called 'degree_inventory' as opposed to a traditional table. My application is written with Laravel, so I source my data using a route that pulls all the objects that correspond to a model and import them into my table. An additional level of complexity exists because the table has 'child rows', but those are not present by default.
My table:
<table id="program-table" class="table stripe compact">
<thead>
<tr>
<th>Levels</th>
<th>CIP Code</th>
<th>CIP Title</th>
<th>USF Title</th>
<th>Degree(s)</th>
<!-- <th>Activated Term</th>
<th>Suspended Term</th>
<th>Discontinued Term</th> -->
</tr>
</thead>
<tbody>
</tbody>
</table>
My DT declaration looks like:
$('#program-table').DataTable({
processing: true,
serverSide: true,
ajax: "{{ route('serverSide') }}",
columns: [
{
"className": 'details-control',
"orderable": false,
"data": null,
"width": '8%',
"defaultContent": ''
},
{ data: 'cip', width: '10%'},
{ data: 'cip_title' },
{ data: 'item_name' },
{ data: 'degree_name_list' }
],
pageLength: 25,
searching: true,
paging: true,
fixedHeader: true, //plugin not working
"order": [[ 1, "asc" ]] //by default order by cip code
});
And my 'serverSide' route that gets called looks like:
Route::get('/serverSide', [
'as' => 'serverSide',
'uses' => function () {
$model = \App\Degree::query();
return DataTables::eloquent($model)->make();
}
]);
The 'Degree' model is entirely empty except for defining the corresponding table to be 'degree_inventory'
The table initially populates, expands child rows, and paginates perfectly, but any query into the search bar returns the following error:
Column not found: 1054 unknown column 'degree_inventory' in 'where clause' followed by the raw query that attempts to find matches to entries in each row.
If anyone has any insight I'd be very appreciative. Thank you!
The issue was that I had a column of buttons to expand and collapse child rows.
The solution was to disable to searchable property of the expand/collapse column because the content of the column is exclusively images.
For example:
$('#example').dataTable( {
"columns": [
{ "searchable": false }, //first column is the expand button
null,
null,
null,
null
] } );
} );

Datatables Child Rows Implementation

Recently, I got a requirement for an implementation of adding Child rows in a table. I have gone through the few APIs and found that datatables fits into my requirement. As of now I am implementing this web application in Springs and getting the data from the controller.
That is $resultSet.
Now this dynamic data I have to render in jsp page. Here I got stucked because I am not able to implement with datatable. I have seen the example of http://datatables.net/examples/api/row_details.html and tried removing Ajax data and used c:foreach loop in place it. But i didn't get any luck.
So can you guys please tell me how do I use datatables with the dyncamic data in order to display child rows.
My main concern is:
$(document).ready(function() {
var table = $('#example').DataTable( {
"ajax": "../ajax/data/objects.txt",
"columns": [
{
"className": 'details-control',
"orderable": false,
"data": null,
"defaultContent": ''
},
{ "data": "name" },
{ "data": "position" },
{ "data": "office" },
{ "data": "salary" }
],
"order": [[1, 'asc']]
} );
How do I represent the above block.
I tried with <table id="xx"> <c:foreach loop to iterate>
You can use data option to feed data into DataTables directly instead of using server-side script to return data via Ajax. This initialization option can be useful when creating a table from a JavaScript data source, or from a custom Ajax data get. However the data has to be of type Array.
I'm not familiar with Spring framework but I'm assuming you can produce a string with data in JSON format and output it in your page to assign to table_data_json. I'm using a sample JSON string var table_data_json = '[ /* skipped */ ]';.
/* Formatting function for row details - modify as you need */
function format ( d ) {
// `d` is the original data object for the row
return '<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">'+
'<tr>'+
'<td>Full name:</td>'+
'<td>'+d.name+'</td>'+
'</tr>'+
'<tr>'+
'<td>Extension number:</td>'+
'<td>'+d.extn+'</td>'+
'</tr>'+
'<tr>'+
'<td>Extra info:</td>'+
'<td>And any further details here (images etc)...</td>'+
'</tr>'+
'</table>';
}
$(document).ready(function() {
var table_data_json = '[{"name":"Tiger Nixon","position":"System Architect","salary":"$320,800","start_date":"2011/04/25","office":"Edinburgh","extn":"5421"},{"name":"Garrett Winters","position":"Accountant","salary":"$170,750","start_date":"2011/07/25","office":"Tokyo","extn":"8422"},{"name":"Ashton Cox","position":"Junior Technical Author","salary":"$86,000","start_date":"2009/01/12","office":"San Francisco","extn":"1562"}]';
var table = $('#example').DataTable( {
"data": JSON.parse(table_data_json),
"columns": [
{
"className": 'details-control',
"orderable": false,
"data": null,
"defaultContent": ''
},
{ "data": "name" },
{ "data": "position" },
{ "data": "office" },
{ "data": "salary" }
],
"order": [[1, 'asc']]
} );
// Add event listener for opening and closing details
$('#example tbody').on('click', 'td.details-control', function () {
var tr = $(this).closest('tr');
var row = table.row( tr );
if ( row.child.isShown() ) {
// This row is already open - close it
row.child.hide();
tr.removeClass('shown');
}
else {
// Open this row
row.child( format(row.data()) ).show();
tr.addClass('shown');
}
} );
} );
td.details-control {
background: url('https://raw.githubusercontent.com/DataTables/DataTables/1.10.7/examples/resources/details_open.png') no-repeat center center;
cursor: pointer;
}
tr.shown td.details-control {
background: url('https://raw.githubusercontent.com/DataTables/DataTables/1.10.7/examples/resources/details_close.png') no-repeat center center;
}
<link href="//cdn.datatables.net/1.10.7/css/jquery.dataTables.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="//cdn.datatables.net/1.10.7/js/jquery.dataTables.min.js"></script>
<table id="example" class="display">
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Salary</th>
</tr>
</thead>
<tfoot>
<tr>
<th></th>
<th>Name</th>
<th>Position</th>
<th>Office</th>
<th>Salary</th>
</tr>
</tfoot>
</table>
If I misunderstood your question, please let me know and I will update my answer.

Durandal and jqGrid

There is already an example of jqxGrid that is labeled "jqGrid integration with existing Durandal solution". However, I don't have the option of using jqxGrid.
Does any one have an example of using jqGrid with durandal. This is what I'm trying now and it is not working.
Unable to parse bindings.
Bindings value: attr: { href: 'animals/' + id, title: name }, text: id
Message: id is not defined;
viewmodel.js
///
define(['durandal/app', 'jqgrid', 'kojqgrid'], function (app) {
var initialData = [
{ id: 1, name: "Well-Travelled Kitten", sales: 352, price: 75.95 },
{ id: 2, name: "Speedy Coyote", sales: 89, price: 190.00 },
{ id: 3, name: "Furious Lizard", sales: 152, price: 25.00 },
{ id: 4, name: "Indifferent Monkey", sales: 1, price: 99.95 },
{ id: 5, name: "Brooding Dragon", sales: 0, price: 6350 },
{ id: 6, name: "Ingenious Tadpole", sales: 39450, price: 0.35 },
{ id: 7, name: "Optimistic Snail", sales: 420, price: 1.50 }
];
var ctor = function () {
this.animals = ko.observableArray([]);
this.disabled = ko.observable(false);
this.activate = function () {
this.animals(initialData);
return true;
}
};
//Note: This module exports a function. That means that you, the developer, can create multiple instances.
//This pattern is also recognized by Durandal so that it can create instances on demand.
return ctor;
});
View
-------------------------------------------------------------------
<h3>Customers</h3>
<table id="animals" data-bind="grid: { data: animals }" >
<caption>Amazing Animals</caption>
<thead>
<tr>
<th data-field="actions" style="width:27px;"></th>
<th data-field="name" width="150px">Item Name</th>
<th data-field="sales">Sales Count</th>
<th data-field="price">Price</th>
</tr>
</thead>
<tbody>
<tr>
<td data-field="actions">
<a class="grid-edit" data-bind="attr: { href: 'animals/' + id, title: name }, text: id"></a>
</td>
</tr>
</tbody>
</table>
Any help would be greatly appreciated.
The reason you are getting id is undefined is because the ctor function does not expose id or name attribute but the animals observableArray. In your view you need to loop over the animals obserbavbleArray to get access to id and name attribute for each animal. Try the following code:
<tbody data-bind='foreach:animals'>
<tr>
<td data-field="actions">
<a class="grid-edit" data-bind="attr: { href: 'animals/' + id, title: name }, text: id"></a>
</td>
</tr>
</tbody>
You need to put your activate handler on the prototype:
ctor.prototype.activate = function () {...}
Durandal likely cannot find your activate handler and, therefore, never initializes the data.
Also, I can only assume that the jqGrid binding is looking at the table definition and harvesting from the DOM what it needs to build a proper binding. The reason I say that is that, strictly speaking, #nimrig is right: there needs to be a foreach somewhere. It must be that the jqGrid is building that foreach.

Resources