In my MVC project, I am trying to use a single Datatable and collapse the rows for detail data as shown on Creating an expandable master-details table (jQuery DataTables and ASP.NET MVC integration - Part IV). On the other hand, I am looking for a similar examples of jQuery Datatable master-detail relations for ASP.NET MVC, but unfortunately I have not another suitable sample or tutorial from at least 50 pages on the web. Is there a similar examples like that? Thanks in advance...
I did similar work for one of the projects. I had one collapse/expand button that works for the whole table and each row has its one collapse expand icon. here is my code.
Note: I have renamed the variables to hide my data so the code might not work as it is.
function populateInstanceTable(tableData){
// Use to determine whether the child rows for all parents should be shown or hidden.
var SHOW_ALL_CHILDREN_FLAG = false;
var CHILD_DISPLAY_STATE_OVERRIDE = false;
var TABLE = $('#table_instance').DataTable(
{
'aaData': tableData,
'bProcessing': true,
'aoColumns': [
{
'sTitle': 'Column1',
'mData' : 'col1Data'
},
{
'sTitle': 'Column2',
'mData' : 'col2Data'
},
{
'sTitle': 'Column3',
'mData': 'col3Data'
},
{
'class': 'show-details',
'orderable': false,
'data': null,
'defaultContent': ''
}
]
}
);
function getDetailContent(details) {
return '<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">' +
'<tr>' +
'<td style="border:0px;">More Details:</td>'+
'<td style="text-align:left;max-width:100%;border:0px;">' + details + '</td>' +
'</tr>' +
'</table>';
}
//This function shows and hides multiple child rows with details, for following conditions
// when user clicks '+' or '-' icon
// When user uses search
// when user changes the number of entries per page
// when user navigates through the table
// #remark: With exception of expand all and collapse all events, the display state is retained for child rows
//that have been previously visited. Visited implies the parent row's show or hide details icon was individually clicked.
function collapseOrExpandRows() {
var numberOfVisibleParentRows = $('#table_instance tbody tr td.show-details').length;
for (var i = 0; i < numberOfVisibleParentRows; i++) {
var parentJQRow = $('.show-details').parents('tr').eq(i);
var parentDTRow = TABLE.row(parentJQRow);
// visited_child helps us retain the state of the child row display while
// searching, navigating, sorting or changing number of entries
// We always change the state of child if collapse all(- icon) or expand all(+ icon) is clicked.
if (parentJQRow.hasClass('visited_child') === false || CHILD_DISPLAY_STATE_OVERRIDE === true) {
if (SHOW_ALL_CHILDREN_FLAG === true) {
// We are populating a child row with a table because the parent datatable does not support colspan property.
parentDTRow.child(getDetailContent(parentDTRow.data().details)).show();
parentJQRow.addClass('shown');
}
else {
parentDTRow.child.hide();
parentJQRow.removeClass('shown');
}
}
}
}
//To display details, this event handler shows or hides a single child row
//when the show-details cell is clicked on the parent row
$('#table_instance tbody').on('click', 'td.show-details', function() {
var parentJQRow = $(this).parents('tr');
var parentDTRow = TABLE.row(parentJQRow);
//visited_child helps us retain the state of the child row display while
// searching, navigating, sorting or changing number of entries
parentJQRow.addClass('visited_child');
if (parentDTRow.child.isShown()) {
parentDTRow.child.hide();
parentJQRow.removeClass('shown');
}
else {
parentDTRow.child(getDetailContent(parentDTRow.data().details)).show();
parentJQRow.addClass('shown');
}
CHILD_DISPLAY_STATE_OVERRIDE = false;
});
// This event handler retains the state of the child row display
// when navigating through the table.
$('.dataTables_paginate').on('click', function() {
collapseOrExpandRows();
});
// This event handler hides child row for all visible parents.
$('.collapseall').on('click', function() {
CHILD_DISPLAY_STATE_OVERRIDE = true;
SHOW_ALL_CHILDREN_FLAG = false;
collapseOrExpandRows();
});
// This event handler shows child row of all visible parents.
$('.expandall').on('click', function() {
CHILD_DISPLAY_STATE_OVERRIDE = true;
SHOW_ALL_CHILDREN_FLAG = true;
collapseOrExpandRows();
});
// This event handler retains the state of the child row display
// when the user selects the number of entries to display in the table
$('div.dataTables_length select').on('change', function() {
collapseOrExpandRows();
});
// This event handler retains the state of the child row display
// when the user clicks on header to sort
$('thead > tr > th', '#table_instance').click(function() {
if ($(this).hasClass('show-details') === false) {
collapseOrExpandRows();
}
});
// This event handler retains the state of the child row display
// when the user searches
$('div.dataTables_filter input').keyup(function() {
collapseOrExpandRows();
});
}
I have attached the screenshot for your help.
There is a good example on Datatables Blog having also a wonderful sliding property. There is unfortunately not so much example on the web regarding to this issue, but I hope this example is useful for you.
I know this is an old thread. But I could not find any good examples since then. I created a working example which is fully scalable using jQuery (3.5.0) and Datatables (1.10.19). Table, columns, rows and sub tables are created on the fly based on data structure.
You can find the demo and code here - https://banglaonline.org/dashboard/temp/dt.html. The js code is in https://banglaonline.org/dashboard/temp/dt.js.
function dataStructure() - creates the first table
function dataStructure(){
$("#content_structure").html("<div id='content_structure_table' style='width:80%'></div>");
var groupBy=[fieldOrder[0].field];//level 1 order
var dtTextColumnIndex=[];
var dtNumColumnIndex=[];
var colCount=1;
var valuefunction=null;//dataCreateValueFunctions();
tblFormatParam = formatTblDataStructure('tblDataStructure',0,"1=1");
html = tblFormatParam[0];
tableDef = tblFormatParam[1];
$("#content_structure_table").html(html);
var table = $('#tblDataStructure').DataTable(tableDef);
//drill down event
$('#tblDataStructure tbody').on('click', 'td.details-control', function(){
e=$(this);
drillTblDataStructure(e,table,valuefunction);
});
$('#tblDataStructure' ).bind( 'xhr.dt', function () {
var json = table.ajax.json();
$('#tblDataStructure').unbind( 'xhr.dt');
//structureChart(json.data);
});
}
function drillTblDataStructure - controls the expansion and contraction of nested tables
function drillTblDataStructure(td,table,valuefunction) {
//console.log("td clicked -"+td.attr("datavalue"));
var tr = td.closest('tr');
var row = table.row( tr );
currentFieldOrder = parseInt(td.attr("fieldorder"));
//close all other open rows
tdDrillControls = document.querySelectorAll("td.details-control");
for (var i=0;i<tdDrillControls.length;i++){
tdi= $(tdDrillControls[i]);
tri = tdi.closest("tr");
r = table.row( tri);
if (r.child.isShown() && (tdi.attr("datavalue") != td.attr("datavalue"))) {//second condition is importatnt, otherwise the row will be keep on showing
r.child.hide();
tri.removeClass('shown');
tdi.html('<span class="ui-icon ui-icon-plus"></span>')
}
}
if (row.child.isShown()) {
// This row is already open - close it
row.child.hide();
tr.removeClass('shown');
td.html('<span class="ui-icon ui-icon-plus"></span>')
}
else {
td.html('<span class="ui-icon ui-icon-minus"></span>')
childFieldOrder = currentFieldOrder +1;
var whereClause = td.attr("whereClause")+" and "+td.attr("datacolumn")+" = '"+td.attr("datavalue")+"'";
var childTableID = childFieldOrder*10+iTableCounter;
tblFormatParam = formatTblDataStructure('tblDataStructure_' + childTableID,childFieldOrder,whereClause);
html = tblFormatParam[0];
tableDef = tblFormatParam[1];
// Open this row
row.child( html ).show();
tr.addClass('shown');
var oInnerTable = $('#tblDataStructure_' + childTableID).DataTable(tableDef);
$('#tblDataStructure_' + childTableID).bind( 'xhr.dt', function () {//table data is loaded via ajax
var json = oInnerTable.ajax.json();
$('#tblDataStructure_' + childTableID).unbind( 'xhr.dt');
});
$('#tblDataStructure_' + childTableID +' tbody').on('click', 'td.details-control', function(){
e=$(this);
drillTblDataStructure(e,oInnerTable,valuefunction);
});
iTableCounter++;
}
}
function formatTblDataStructure - used by the above two functions to create table structure and definition for Datatables
function formatTblDataStructure ( table_id, groupOrder, whereClause) {
var dtTextColumnIndex=[];
var dtNumColumnIndex=[];
var colCount=1;
if(groupOrder<fieldOrder.length-1) //it is not the last item in the order
var tdColumns=[
{ className: 'details-control',
orderable: false,
data: null,
defaultContent: '<span class="ui-icon ui-icon-plus style="cursor:pointer;"></span>'
}
];
else
var tdColumns=[
{ className: '',
orderable: false,
data: null,
defaultContent: ''
}
];
tdColumns.push({data:fieldOrder[groupOrder].field});
html="<table id='"+table_id+"' ><thead><tr><th></th>";
thtml="<tr><td></td>";
html+="<th>"+fieldOrder[groupOrder]["dataField"]+"</th>";
thtml+="<td></td>"
dtTextColumnIndex.push(colCount);
colCount++;
var valuefunction=[];
for(f in valueFields){//getting field names from json column names
html+="<th>"+valueFields[f]["dataField"]+"</th>";
thtml+="<td></td>"
dtNumColumnIndex.push(colCount);
colCount++;
txt = valueFields[f].groupFunction+"("+valueFields[f].field+") as `"+valueFields[f].field+"`";
valuefunction.push(txt);
tdColumns.push({data:valueFields[f].field});
}
html+="</tr></thead><tbody>"+thtml+"</tr></tbody></table>";
groupBy=[fieldOrder[groupOrder].field];
var tableDef={
"ajax":{
url: src,
data:{
request:'dataStructure'
,dbid:sessionID
,group:JSON.stringify(groupBy)
,where:whereClause
,value:JSON.stringify(valuefunction)
,dt:"assoc"
}
},
createdRow: function( row, data, dataIndex ) {
$( row ).find('td:eq(0)').attr(
{
'fieldOrder': groupOrder,
'datavalue': data[fieldOrder[groupOrder].field],
'datacolumn': fieldOrder[groupOrder].field,
'whereClause': whereClause
}
);
},
columns: tdColumns,
columnDefs: [
{
targets: dtTextColumnIndex,
className: 'dt-body-left dt-head-left'
},
{
targets: dtNumColumnIndex,
className: 'dt-body-right dt-head-right'
//,"render": function ( data, type, row, meta ) {
// return infGroup.format(data);
//}
}
]
}
return [html,tableDef];
}
In the example, data is retrieved as json via ajax from a php backend. Example json response:
{
"data": [
{ "dash_uploaded_data_text1": "Dept 1", "dash_uploaded_data_numeric1": "3", "dash_uploaded_data_numeric2": "11" },
{ "dash_uploaded_data_text1": "Dept 4", "dash_uploaded_data_numeric1": "4", "dash_uploaded_data_numeric2": "8" }
]
}
I'm having trouble changing the class after making a jquery get request.
code:
<script>
//global variable
var table = []
var numberofaccounts = 0
$(document).ready(function() {
$('#form1').validate();
// add numbers to select ids
$(".select_changer").each(function(){
numberofaccounts++;
var new_id = "select_class"+numberofaccounts;
$(this).addClass(new_id);
});
$('#apply_btn').click(function() {
table = []
var count = 0;
var text = "";
var tracker = 0
$('#stats_table tr').each(function(){
count = 0;
text = "";
$(this).find('td').each(function(){
count++;
if (count == 4) {
text += $( ".select_class"+ tracker + " option:selected" ).val();
} else {
text += " " + $(this).text() + " ";
}
})
table.push(text);
tracker++;
});
$.post("/apply_changes", {"data": JSON.stringify(table)}, function(data) {
var res = JSON.parse(data);
if (res.data == true){
$('#updated').text("Update Successful").css('color', 'green');
$.get("/", function( data ) {
$('#stats_table').load("/ #stats_table");
numberofaccounts = 0
$(".select_changer").each(function(){
numberofaccounts++;
var new_id = "select_class"+numberofaccounts;
$(this).addClass(new_id);
});
});
} else {
$('#updated').text("Update Unsuccessful").css('color', 'red');
}
});
});
});
</script>
So when the page first loads this method changes the class on dynamically created select elements.
$(".select_changer").each(function(){
numberofaccounts++;
var new_id = "select_class"+numberofaccounts;
$(this).addClass(new_id);
});
After I make a post to flask the if the response data is true I go ahead and make a get request to grab the updated items from the db. I then refresh the table. This works great if I make one request. However on the second post nothing happens. This is because the classes that I modified at the start of the page load no longer exist. So i added the method above to also trigger after the get response (I also tried at the end of the post response). The problem is that the method doesn't seem to run again. The classes aren't there and as a result when I go to make another post request it can't find the element. How do I go about fixing this?
Things to note: The get request is necessary, the ids and classes cannot be statically assigned.
You are trying to assign classes before you even refresh your table.
$('#stats_table').load("/ #stats_table"); is called asynchronously and returns before it even completes.
You need to put you code, for assigning classes, inside the complete callback of your .load() call:
$('#stats_table').load("/ #stats_table", function() {
numberofaccounts = 0
$(".select_changer").each(function(){
numberofaccounts++;
var new_id = "select_class"+numberofaccounts;
$(this).addClass(new_id);
});
});
I am building a smartphone app using localStorage. Everything works well until I make an action. Here's the JS:
var ls = localStorage,
$input = $('#add-movie'),
$content = $('.content'),
$tools = $('.tools');
var movy = {
// returns the next id
// prev id is stored in ls
nextId: function() {
var i = ls.getItem('index') || 0;
i++;
ls.setItem('index', i);
return i;
},
getRelease: function(name, cb) {
cb('12/4'); // only temporary
},
// adds a new movie to the localStorage
new: function(name) {
var data = {
name: name
};
movy.getRelease(name, function(results) {
data.release = results;
});
ls.setItem(movy.nextId(), JSON.stringify(data));
},
// show all films
renderAll: function() {
$tools.hide();
var html = '';
for (var i = 1; i < ls.length; i++) {
var item = JSON.parse(ls.getItem(i));
if (!item) { }
else
html += '<tr data-index="' + i + '"><td class="name">' + item.name + '</td><td class="date">' + item.release + '</td></tr>';
}
$content.html(html);
},
remove: function(key) {
ls.removeItem(key);
for (var i = key + 1; i <= ls.length; i++) {
var item = ls.getItem(i);
ls.setItem(i - 1, item);
}
// decriment i
var index = ls.getItem('index');
index--;
ls.setItem('index', index);
}
}
$(function() {
movy.renderAll();
$('form').submit(function(e) {
e.preventDefault();
movy.new($input.val());
console.log($input.val());
movy.renderAll();
console.log('rendered');
});
$('.content tr').click(function() {
$(this).toggleClass('checked');
$tools.toggle();
$tools.find('#trash').attr('data-index', $(this).attr('data-index'));
});
$('#trash').click(function() {
var i = $(this).attr('data-index');
console.log(i);
movy.remove(i);
movy.renderAll();
// now nothing works until page is refreshed
});
});
Now, at the very first time when I refresh the page, it responds to clicks, shows the toolbar when needed and everything is great. However, after I click on trash, and it successfully deletes that item and re-renders all the elements, suddenly jQuery stops listening for clicks, and the whole thing becomes not responsive. That is, until I refresh the page.
Thanks!
Making my comment that solved the problem into an answer:
If you are rebuilding all the DOM elements (e.g. making new DOM elements), then your event handlers were bound to the old DOM elements and there are no event handlers on the new DOM elements.
You have to either use delegated event handling (attaching event handlers to static parent objects) or assign new event handlers to the newly create DOM elements. See this answer for how to do delegated event handling.
Can anyone tell me why my IF statement is firing before updating the UI with the each loop?
The code basically wants to delay adding css classes to the UI then once each one has been added, redirect the user. It currently just directs immediately?!
$("#logo").click(function() {
//define variables:
var eventDuration = 500;
var elementArray = ['ribbon', 'left-panel', 'wid-id-1', 'wid-id-2'];
var animationArray = ['slideOutRight', 'slideOutLeft', 'rotateOutUpRight', 'rotateOutUpRight'];
//Loop through elements and update UI with timer function:
$.each(elementArray, function(index, value) {
//Increments the delay of the element updates:
var delaytimer = index * eventDuration + eventDuration;
//Adds animation css classes to onpage elements:
$('#' + value).delay(delaytimer).queue(function() {
$(this).addClass('animated ' + animationArray[index]).dequeue();
});
//Once complete redirect to the home page:
if (index === 3) {
$(this).delay(delaytimer + 500).queue(function() {
window.location.replace('/').dequeue;
});
}
});
});
Your if statement is being executed immediately because it isn't inside the delay function. Try moving it in there.
$('#' + value).delay(delaytimer).queue(function() {
$(this).addClass('animated ' + animationArray[index]).dequeue();
//Once complete redirect to the home page:
if (index === 3) {
$(this).delay(delaytimer + 500).queue(function() {
window.location.replace('/').dequeue;
});
}
});
I have a page which is dynamically generated and uses slideToggle to open and close the hierarchical divs etc no problem. The only problem is, everytime I postback I have to generate the divs again and they lose their opened/closed state. They are always generated with the same unique ids.
I would like to use the cookie plugin to remember the states when I call my sltoggle function and then when the page reloads expand all the same divs. Heres what i've got so far...
$(document).ready(function ()
{
$(".toggle-hide").hide();
//something in here about opening the divs in the cookie
});
function sltoggle(eID)
{
$("div[id$='" + eID + "']").slideToggle(600);
//I think the below code is okay - I copied it from a working example ^^
var divState = ($("div[id$='" + eID + "']").css('display') == 'block') ? 1 : 0;
$.cookie("divState", state)
}
Comment explanations inline.
function slToggle(eID) {
var $div = $("div[id$='" + eDI + "']");
//Get value of cookie or empty string
//Cookie is list of eIDs that should be visible
var cooks = $.cookie("divState") || '';
//Determine whether eID is already in the cookie
var isin = $.inArray(eID, cooks.split(','));
//TODO verify that .is("visible") check works during
//toggle animation. Otherwise, this code goes in the
//toggle animation callback function
if ($div.slideToggle(600).is(":visible")) {
//Div is visible, but not in cookie
if (!isin) {
$.cookie("divState", cooks + (cooks ? ',' : '') + eID);
}
}
else if (isin) {
//Div not visible, but in cookie
$.cookie("divState", cooks.replace(/(^|,)eID(,|$)/, ''));
}
}