Wanting to use DataTables to nest a table within a table...within a table...can it be done? - datatables

I just started using dataTables so I'm hoping this will be a learning opportunity.
I work for a school system and trying to develop a table that will show a student's grades from year to year. I want an administrator to be able to click on any year, and the table will expand to show a child table with the student's GPA for each term (6 terms per school year). Finally, I want the administrator to be able to click on any term and show a child table for actual classes the student took that term and their grade.
I'm able to successfully get the term table to show, but I'm having no luck getting the course table to show. I'm hoping it's not a case of "you can't nest a table within a table within a table", but I can't figure out how to make it stick, although I'm pretty sure it has to do with the "details-control" class...
Here is a link to a fiddle I put together showing what I have...any help finishing it up would be greatly appreciated!!
fiddle me this
var iTermGPACounter = 1;
$(document).ready(function() {
loadDetailsByCourse();
});
function loadDetailsByCourse() {
var table = $('#msGrades').DataTable({
data: [{
"__type": "DMC.WebServices.detailGPA",
"schoolYearLabel": "14-15",
"schoolLevel": "02",
"location": "Highland",
"grade": "7",
"gpaValue": "3.119",
"termGPA": [{
"term": "1",
"gpaValue": "3.857",
"termCourseGrades": [{
"courseNo": "38929712",
"sectionNo": "200",
"courseName": "HEALTH 2",
"letterGrade": "A+",
"department": "EL"
}, {
"courseNo": "32320711",
"sectionNo": "10",
"courseName": "LANG ARTS 2",
"letterGrade": "A+",
"department": "EL"
}, {
"courseNo": "32720711",
"sectionNo": "10",
"courseName": "MATH 2",
"letterGrade": "B",
"department": "MA"
}]
}, {
"term": "2",
"gpaValue": "3.714",
"termCourseGrades": [{
"courseNo": "38929712",
"sectionNo": "200",
"courseName": "HEALTH 2",
"letterGrade": "A",
"department": "EL"
}, {
"courseNo": "32320711",
"sectionNo": "10",
"courseName": "LANG ARTS 2",
"letterGrade": "A",
"department": "EL"
}, {
"courseNo": "32720711",
"sectionNo": "10",
"courseName": "MATH 2",
"letterGrade": "A-",
"department": "MA"
}]
}]
}, {
"__type": "DMC.WebServices.detailGPA",
"schoolYearLabel": "15-16",
"schoolLevel": "02",
"location": "Highland",
"grade": "8",
"gpaValue": "3.123",
"termGPA": [{
"term": "1",
"gpaValue": "3.143",
"termCourseGrades": [{
"courseNo": "32320711",
"sectionNo": "12",
"courseName": "LANG ARTS 2",
"letterGrade": "A",
"department": "EL"
}, {
"courseNo": "32720711",
"sectionNo": "12",
"courseName": "MATH 2",
"letterGrade": "D",
"department": "MA"
}]
}]
}],
paging: false,
columns: [{
className: 'details-control',
orderable: false,
data: null,
defaultContent: '<img src="http://i.imgur.com/SD7Dz.png">'
}, {
data: "schoolYearLabel"
}, {
data: "grade"
}, {
data: "location"
}, {
data: "gpaValue"
}],
order: [
[1, 'asc']
]
});
// Add event listener for opening and closing details
$('#msGrades 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(formatTermGPA(iTermGPACounter)).show();
tr.addClass('shown');
var oInnerTable = $('#termGPA_' + iTermGPACounter).dataTable({
data: row.data().termGPA,
paging: false,
searching: false,
columns: [{
className: 'details-control',
orderable: false,
data: null,
defaultContent: '<img src="http://i.imgur.com/SD7Dz.png">'
}, {
data: "term"
}, {
data: "gpaValue"
}],
order: [
[1, 'asc']
]
});
// Add event listener for opening and closing details
$('#termGPA_' + iTermGPACounter + ' 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(formatTermCourseGrades()).show();
tr.addClass('shown');
}
});
iTermGPACounter += 1;
}
});
}
function formatTermGPA(table_id) {
return '<table class="table table-striped" id="termGPA_' + table_id + '">' +
'<thead><tr><th></th><th>Term #</th><th>Term GPA</th></tr></thead></table>';
}
function formatTermCourseGrades() {
return '<table class="table table-striped" id="termCourseGrades">' +
'<thead><tr><th>Course Name</th><th>Letter Grade</th></tr></thead><tr><td>Math</td><td>B+</td></tr></table>';
}

I posted this question in the DataTables forum as well and received an answer...in a nutshell, as a starting point I copied from the DataTables examples a table with a single child table. When I added the second child table, I didn't think to rename the variables, so it kept calling back to the parent table :(. Below is a working fiddle of the final result:
working fiddle
function loadDetailsByCourse() {
$.ajax({
type: "POST",
contentType: "application/json; charset=UTF-8",
url: "WebServices/myStudentProfile.asmx/getDetailsByCourse",
data: JSON.stringify({ pageID: pageID }),
dataType: "json",
cache: false,
success: function (response) {
gpa = response.d;
var yearTable = $('#msGrades').DataTable({
paging: false,
data: gpa,
columns: [
{
className: 'term-details-control openClose',
orderable: false,
data: null,
defaultContent: ''
},
{ data: "schoolYearLabel" },
{ data: "grade" },
{ data: "location" },
{ data: "gpaValue" }
],
order: [[1, 'asc']]
});
// Add event listener for opening and closing details
$('#msGrades tbody').on('click', 'td.term-details-control', function () {
var tr = $(this).closest('tr');
var row = yearTable.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(formatTermGPA(iTermGPACounter)).show();
tr.addClass('shown');
var termTable = $('#termGPA_' + iTermGPACounter).DataTable({
data: row.data().termGPA,
paging: false,
searching: false,
columns: [
{
className: 'course-details-control openClose',
orderable: false,
data: null,
defaultContent: ''
},
{ data: "term" },
{ data: "gpaValue" }
],
order: [[1, 'asc']]
});
// Add event listener for opening and closing details
$('#termGPA_' + iTermGPACounter + ' tbody').on('click', 'td.course-details-control', function () {
var tr = $(this).closest('tr');
var closestTable = tr.closest("table");
var row = closestTable.DataTable().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(formatTermCourseGrades(iCourseCounter)).show();
tr.addClass('shown');
var courseTable = $('#courseGrades_' + iCourseCounter).DataTable({
data: row.data().termCourseGrades,
paging: false,
searching: false,
columns: [
{ data: "courseName" },
{ data: "letterGrade" }
],
order: [[1, 'asc']]
});
}
iCourseCounter += 1;
});
iTermGPACounter += 1;
}
});
}
})
}
function formatTermGPA(table_id) {
return '<table class="table table-striped" id="termGPA_' + table_id + '">' +
'<thead><tr><th></th><th>Term #</th><th>Term GPA</th></tr></thead></table>';
}
function formatTermCourseGrades(table_id) {
return '<table class="table table-striped" id="courseGrades_' + table_id + '">' +
'<thead><tr><th>Course Name</th><th>Letter Grade</th></tr></thead></table>';
}

Related

DataTable row returns undefined

I have a JQuery DataTable. Fpr each row I have a button and when the user clicks on it, it should show related fields in a modal in order to edit that row. Now my problem is I can't get the selected row id and it returns undefined. Here is my code:
$("#myDummyTable").DataTable({
"processing": true, // for show progress bar
"serverSide": true, // for process server side
"filter": true, // this is for disable filter (search box)
"orderMulti": false,
//"searching": false,
"language": {
"url": "/language/Persian.json"
},
"ajax": {
"url": "Data/GetData",
"type": "POST",
"datatype": "jason"
},
"columnDefs": [{
"targets": [0],
"visible": false,
"searchable": false
}],
rowId: "id",
"columns": [
{ "data": "id", "name": "Id", "autoWidth": true },
{ "data": "startDate", "name": "StartDate", "autoWidth": true },
{ "data": "endDate", "name": "EndDate", "autoWidth": true },
{
defaultContent: '<input type="button" class="Edit" value="edit"/>
}
]
});
$('#myDummyTable tbody').on('click', '.Edit', function () {
var row = myDummyTable.row(this).rowId;
var myDummyTable = $('#myDummyTable').DataTable();
console.log('row:' + myDummyTable.row(this).rowId);
});
});
Instead of using the defaultContent column option, you can use a render function. This allows you to customize each button so it can include the relevant ID:
{ data: "id", render: function ( data, type, row, meta ) {
return '<input type="button" class="Edit" id="' + data + '" value="edit id ' + data + '"/>';
}
}
Here we first use data: id again (same as for the first column). This means this column will use the id value.
Then we use the render function which gives us access to the id value via the data variable.
In my example I added the ID to the text used by each button.
I also added an id attribute to each of these cells, using the same data value. This assumes each ID is unique, of course.
My test data - an example of one row:
{
"id": "5",
"name": "Airi Satou",
"start_date": "2008/11/28",
"office": "Tokyo"
}
In this case, Airi Satou's ID is 5.
My test table:
Now, when I click on a button, the following code will print the related ID:
$('#myDummyTable tbody').on('click', '.Edit', function () {
console.log( this.id );
});

VueJS implement go to prev menu

I am writing an app where a fixed length list gets generated based on the nested JSONArray. Whenever any elements gets clicked from the list and if it has a "sub data" array,the list gets populated with this "sub data". Basically, you can think of it as menus which has submenus and those submenus has subsubmenus and so on.
I have implemented two methods for going to sublevels [next()] which works fine but I don't know how to implement prev() method to go one level up in the menu. Currently, I can make it go one level up but if user is inside more than two level then I don't know how to keep the track of all above levels.
Here is the codepen -
codepen
let JSONModel = (_id, _lvl, _title, _data) => {
return {
id: _id,
lvl: _lvl,
title: _title,
data: _data
};
};
let Menu = [
{
id: "01",
lvl: "01",
title: "menu 1",
data: []
},
{
id: "02",
lvl: "01",
title: "menu 2",
data: []
},
{
id: "03",
lvl: "01",
title: "menu 3",
data: []
},
{
id: "04",
lvl: "01",
title: "menu 4",
data: [
{
id: "01",
lvl: "02",
title: "submenu 1",
data: []
},
{
id: "02",
lvl: "02",
title: "submenu 2",
data: [
{
id: "01",
lvl: "03",
title: "sub submenu 1",
data: []
},
{
id: "02",
lvl: "03",
title: "sub submenu 2",
data: []
},
{
id: "03",
lvl: "03",
title: "sub submenu 3",
data: []
},
{
id: "04",
lvl: "03",
title: "sub submenu 4",
data: []
}
]
},
{
id: "03",
lvl: "02",
title: "submenu 3",
data: []
},
{
id: "04",
lvl: "02",
title: "submenu 4",
data: []
}
]
}
];
let demo = new Vue({
el: "#app",
data: {
input: Menu,
prevMenu:[]
},
computed: {},
created: function () {
},
methods: {
next: function(val1,val2) {
if (val1.length != 0) {
this.input = val1;
this.prevMenu = val2;
console.log(this.prevMenu);
}
},
prev: function() {
console.log(this.prevMenu);
this.input = this.prevMenu;
}
}
});
$("#prevmenu").on("click", function() {
demo.prev();
});
Using your code, you can simply do this:
https://codepen.io/webkit_il/pen/bjebZR?editors=0011
next: function(val1,val2) {
if (val1.length != 0) {
this.input = val1;
this.prevMenu.push(val2);
}
},
prev: function() {
let _menu = this.prevMenu.slice(); // this is just to clone the array
this.input = _menu[_menu.length - 1];
this.prevMenu.pop();
}
changed your prevMenu into an array, then everytime you
go back just use the last one, and remove it from the array...
good luck!

DataTables: createdCell doesn't work on page load (after it works)

I'm using datatables and I'd like to show an icon in a column (based on the value in the column data).
I've written a code like this:
$(document).ready(function() {
var oTable = $("#initiativeTable").DataTable({
"serverSide":true,
"processing":true,
"searching": false,
"ordering": true,
"order": [[ 0, "desc" ]],
"ajax": '${basePath}/cns/initiative/table.json?teams=${teams}&fields=${fields}&search=${search}',
"columns": [
{ "data": "id_init" },{ "data": "parent_name_init" },{ "data": "name_cust" },{ "data": "name_init" },{ "data": "code_paf" },{ "data": "name_team" },{ "data": "pa_name_emp" },{ "data": "pgm_name_emp" },{ "data": "pm_name_emp" },{ "data": "id_initstt" },{ "data": "description_contrtype" },{ "data": "description_inittype" },{ "data": "is_revised_init" }] ,
"columnDefs": [{ "targets": 12,
"createdCell": function (td, cellData, rowData, row, col) {
if ( cellData == 'false' ) {
$(td).html("");
}else{
$(td).html("<span class='glyphicon glyphicon-ok'></span>");
}
}
}
});
});
The DataTables works fine but the column show the value of data, not the icon. If I reorder the table or go on second page of pagination, the createdCell works and the icon is showed.
It doesn't work only on the first load of the page.
What is the problem?
Thanks in advance
Cheers
Matteo
The syntax of createdCell is Inside columns, check columns.createCell:
columns: [
{
data : "name_data",
createCell: function(){
//code
}
}
]
You can use this code:
{
"data": "is_revised_init",
"createdCell": function (td, cellData, rowData, row, col) {
if ( cellData == 'false' ) {
$(td).html("");
}else{
$(td).html("<span class='glyphicon glyphicon-ok'></span>");
}
}
}

Datatables Editor reports "TypeError f is undefined" when updating record server->client

Trying to hook up a client/server interface for record updates. Alles gute until the very last mile: after returning the response to the client I get this error:
TypeError: f is undefined
dataTables.editor.js (line 252 col 138)
The response is:
{"data":
[{"planid":null,"evnamelast":"Duck","eveligibleincome":3232,"DT_RowId":10003869,"evnamefirst"
:"Daffy","estart":1440054000000,"eligibilityversionid":10003869,"evpositionname ":"Duck duck goose"}] }
The columns are setup as follows:
var dispCols = [{
data: null,
defaultContent: '',
className: 'select-checkbox',
orderable: false
},
{ data: "evnamefirst" },
{ data: "evnamelast" },
{ data: "evpositionname" },
{ data: "planid" },
{ data: "estart" },
{ data: "eveligibleincome", render: $.fn.dataTable.render.number( ',', '.', 0, '$' ) }
];
And the DataTables setup as:
//Define the Editor
editor = new $.fn.dataTable.Editor( {
ajax: {
"url": "/grid?pAction=UpdateRecs&recType=" + recType,
"dataSrc": ""
},
idSrc: "eligibilityversionid",
table: "#" + GRID_ID,
/*fields: columns */
fields: fields
} );
//Setup DataTable
var table = $GRID.DataTable( {
dom: "Bfrtip",
ajax: {
"url": "/grid?pAction=GetRecords&recType=" + recType,
"dataSrc": ""
},
columns: dispCols,
select: {
style: 'os',
selector: 'td:first-child'
},
buttons: [
{ extend: "create", editor: editor },
{ extend: "edit", editor: editor },
{ extend: "remove", editor: editor }
]
} );
Thanks in advance!
Asked this back in October 15, now it is August 2016. So I am going to answer this myself:
Use handsontable.

render jquery datatable boolean column with check and x

How do you render a boolean true/false value coming from JSON to a green check or a red x on a jquery datatable?
For example, something like:
&check;
&check;
and
&cross;
&cross;
Using bootstrap glyphicons you can do this:
personTable = $("#person-table").DataTable({
order: [1, "desc"],
"autoWidth": false,
ajax: {
url: uri,
dataSrc: "",
},
"columns": [
{ "data": "FirstName", "title": "Name" },
{ "data": "Address", "title": "Address" },
{ "data": "IsActive", "title": "Active" }
],
"columnDefs": [
{
"render": function (data, type, row) {
return row.FirstName + " " + row.LastName;
},
"targets": 1
},
{
"render": function (data, type, row) {
return (data === true) ? '<span class="glyphicon glyphicon-ok"></span>' : '<span class="glyphicon glyphicon-remove"></span>';
},
"targets": 2
}
]
});
Then add some css like this:
/* Green check. */
.glyphicon-ok {
color: green;
}
/* Red X. */
.glyphicon-remove {
color: red;
}
For my purposes I am ok with adding this custom CSS to a pre-defined bootstrap icon. If you don't want that, define and use your own class.