DataTables logo DataTables

Drill-down data
  • Hello all,

    A new thread for a new blog post :-) http://datatables.net/blog/Drill-down_rows . In this post I show how a details row in the table can be controlled by the end user through the API, with help from a couple of new features in DataTables 1.8 and a nice display animation.

    Enjoy!
    Allan
  • Hey Allan, first off thank you for the time and support that you put into this plugin! My donation is on its way.

    I have modified this slightly to remove the td image and changed it to activate the details div based upon clicking anywhere on the row, below is my working code

    <code>
    $('#example tbody tr').live( 'click', function () {

    var nTr = this;
    var i = $.inArray( nTr, anOpen );

    if ( i === -1 ) {
    $(this).addClass('row_selected');
    var nDetailsRow = oTable.fnOpen( nTr, fnFormatDetails(oTable, nTr), 'details' );
    $('div.innerDetails', nDetailsRow).slideDown();
    anOpen.push( nTr );
    }
    else {
    $(this).removeClass('row_selected');
    $('div.innerDetails', $(nTr).next()[0]).slideUp( function () {
    oTable.fnClose( nTr );
    anOpen.splice( i, 1 );
    } );

    }
    } );
    </code>

    I have added a row_selected class to the <tr> so that I can change the open row's color. I have seen this question about row opening asked on many other forum posts so hopefully this can help others.

    One issue I have been having is I would like to only have one row "open" at a time. This is because I am using a form to post php to mysql inside my details div and with multiple rows open I have similar DOM elements that cause me problems with the form submission. I know I could probably dynamically generate the form id's so they would be able to be identified separately in the DOM but this is not needed for my implementation.

    Could I get an example of how I would check the anOpen array and just close all rows before opening a new one? I assume I could just add this as the first command in the open row check so that I would only have one details form in the DOM at one time. Thanks again for your help!

    Mike
  • Hi Mike,

    Chytkam said: One issue I have been having is I would like to only have one row "open" at a time

    This can be achieved by checking to see if there are any rows already open in the anOpen array and if so, then close them. Something like this:

            $('#example td.control').live( 'click', function () {
               var nTr = this.parentNode;
               var i = $.inArray( nTr, anOpen );
               
               $(anOpen).each( function () {
                 if ( this !== nTr ) {
                   $('td.control', this).click();
                 }
               } );
               
               if ( i === -1 ) {
                  $('img', this).attr( 'src', sImageUrl+"details_close.png" );
                  var nDetailsRow = oTable.fnOpen( nTr, fnFormatDetails(oTable, nTr), 'details' );
                  $('div.innerDetails', nDetailsRow).slideDown();
                  anOpen.push( nTr );
                }
                else {
                  $('img', this).attr( 'src', sImageUrl+"details_open.png" );
                   $('div.innerDetails', $(nTr).next()[0]).slideUp( function () {
                     oTable.fnClose( nTr );
                     anOpen.splice( i, 1 );
                   } );
                 }
             } );
    	} );
    

    The only new part there is the "$(anOpen).each( function () {" block which will simply close any open rows which are not the target.

    Regards,
    Allan
  • What if a details row is opened (using the above code) and the user navigates to the next page of the table, then opens a new details row. The one on the previous page will still remain open - is it because you're closing the details row with a simulated click?
  • I believe I figured out my own solution (using the same simulated click logic):

    var oTable = $('#example').dataTable({
    	"fnPreDrawCallback": function (oSettings) {
    		$("td.control img[src$='details_close.png']").click();
    	}
    });
    
  • I'm integrating this into http://www.modeltraintracker.com/sandbox/test_item.php but am having the following issue:
    If you click the expansion td to expand info out, the "edit" and "delete" buttons are enabled, as they should be. If you click on another row's expansion td, the old row collapses, the new row expands, but the "edit" for the new row is not enabled. It would appear that the "click" listener, being a "live" event, is handled serially, and the old row is collapsed after the new row expanded, thus disabling the edit button. What is the best way around this?

    Edit: here's my code:
    $('#item_table td.control').live( 'click', function () {
    			var nTr = this.parentNode;
    			var i = $.inArray( nTr, anOpen );
    
    			$(anOpen).each( function () {
    				if ( this !== nTr ) {
    					$('td.control', this).click();
    				}
    			} );
    			
    			if ( i === -1 ) {
    				$('img', this).attr( 'src', sImageUrl+"details_close.png" );
    				$('div.innerDetails', nDetailsRow).slideDown();
    				var nDetailsRow = oTable.fnOpen( nTr, fnFormatDetails(oTable, nTr), 'details' );
    				anOpen.push( nTr );
    				$(nTr).addClass('row_selected');
    				clickedRowId = $(nTr).attr('id');
    				$('#btnEditRow').button( "option", "disabled", false );
    			} else {
    				$('img', this).attr( 'src', sImageUrl+"details_open.png" );
    				$('div.innerDetails', $(nTr).next()[0]).slideUp( function () {
    					$(nTr).removeClass('row_selected');
    					clickedRowId = 0;
    					oTable.fnClose( nTr );
    					anOpen.splice( i, 1 );
    					$('#btnEditRow').button( "option", "disabled", true );
    				  } );
    			}
    			return false;
    		} );
  • Hey Allen,

    This is a great plugin and excellent blog post. I wonder if you (or someone out there) can help me use this example for a table that does not use an ajax for a datasource.

    I have a standard html table with about 20-25 items in it. I've applied the datatable to it and everything looks good.

    When I added the code to add row details as shown in the blog post, I can't quite figure out how to approach the problem because I'm not sure how to correctly use the fnOpen function with HTML content that already exists somewhere in the DOM. Should I add all the row details elsewhere in the page and call them when needed?

    Thanks in advance for any help.
  • I'm following the code in the blog and would like to know how to obtain a value of a column in the row that was just clicked to reveal the details. What I have is an ID in the second column (immediately to the right of the '+' image. I'd like to have the value of that id so that I can then pull relevant data for that entity to be displayed in the details row.

    I've tried:

    var aData = oTable.fnGetData( nTr );
    var id = aData[1];
    

    but that didn't work. id is always 'undefined'.

    Thanks in advance.
  • Never mind, I figured it out.
  • I wonder if in these cases the solution would not be of interest to others...
  • OK. If anyone cares, my solution had nothing to do with aData. I was recycling old code and aData just wasn't required. What I ended up doing was using:

    var id = $(nTr.children[1]).html();
    

    where children[1] happens to be the column where I store the ID. If there's a better way, do tell...
  • Hi, I am not able to show the detail view. I had a link saying View Details instead of a Image.

    I have a datatable which has JSON data, I added a link to see the details. When I am clicking on the link, nothing happening.

    Thanks,

    Below is my code:

    [code]
    <script type="text/javascript">

    var dataTable;
    $(function () {

    $("#GetUserBtn").click(function (e) {
    e.preventDefault();
    var postUrl = $("#UserInfo").attr("action");
    var roleCode = $("#RoleCode").val();

    if (roleCode.length <= 0) {
    $(".roleValidation").show();
    return false;
    }
    else {
    $(".roleValidation").hide();
    $.post(postUrl, { "roleCode": roleCode }, function (data) {

    var userData = data.Data != null ? data.Data : [];

    dataTable = $('#usersTable').dataTable({
    "bJQueryUI": true,
    "aaData": userData,
    "oLanguage": {
    "sZeroRecords": "No Records Found"
    },
    "bDestroy": true,
    "aoColumns": [{ "sTitle": "Full Name", "mDataProp": "FullName" }, { "sTitle": "UserName", "mDataProp": "UserName" }, { "sTitle": "Email", "mDataProp": "Email" }, { "sTitle": "Role", "mDataProp": "RoleName" }, { "sTitle": "View Details", "mDataProp": null, "sDefaultContent": '<a href="#">View Details</a>'}]

    });

    });
    return false;
    }
    });
    });

    $('#usersTable td.control').live('click', function () {
    var nTr = this.parentNode;
    var i = $.inArray(nTr, anOpen);

    if (i == -1) {
    //$('a', this).attr( 'src', sImageUrl+"details_close.png" );
    oTable.fnOpen(nTr, fnFormatDetails(oTable, nTr), 'details');
    anOpen.push(nTr);
    }
    else {
    //$('img', this).attr( 'src', sImageUrl+"details_open.png" );
    oTable.fnClose(nTr);
    anOpen.splice(i, 1);
    }
    });

    function fnFormatDetails(oTable, nTr) {
    var oData = oTable.fnGetData(nTr);
    var sOut =
    '<div class="innerDetails">' +
    '<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">' +
    '<tr><td>Full Name:</td><td>' + oData.FullName + '</td></tr>' +
    '<tr><td>UserName:</td><td>' + oData.UserName + '</td></tr>' +
    '<tr><td>Email:</td><td>' + oData.Email + '</td></tr>' +
    '</table>' +
    '</div>';
    return sOut;
    }
    </script>
    }


    </script>
  • Trying to figure out how the drill-down data piece works. What I have now is a regular table that just unhides rows when they are clicked on.

    Am I reading the example correct, do I have to essentially populate a js array with all the drop down values?

    On that same note, only 1/4 of the fields will have a drop down available, so does that mean I have to pass in a bunch of blank values?

    Thanx ahead of time.
  • Hi Allan, great job. I'm new here and I've been able to successfully implement a drill down dataTable. However, I have a challenge...how can I close and slide up a row from within the drill down? I have added a button within the drill down, but I'm not sure how to make sure I close the exact row I'm on. Can you help? Thanks.
  • What you need to do is basically the same as the code used in the example to close the row (see the part where fnClose is called). But that will need to be called by your button inside the details row. You'll probably be best using live events for that.

    Allan
  • Thanks for the reply Allan. Appreciated.
  • What's the best way to get the details html from an external url?

    oTable.fnOpen( nTr, $(this).load('http://www.google.com/';), 'details' );
  • I'm building a drill-down dataTable from an Ajax source, and am having trouble implementing a custom row filter on top of it. The general filter works fine, but I can't make the custom filter code work in conjunction with the code that populates and scrolls up/down for the inner table. Can you provide any help? Basically I'd like to figure out how to build a range filter and/or a pull-down filter based on the data within a certain column. Thanks in advance!
  • @jbrahy - Probably using a proxy script on your own server, so you don't run into problems with cross domain security concerns.

    @writermgb - This is how you build a range filter: http://datatables.net/release-datatables/examples/plug-ins/range_filtering.html - but I don't really understand how you would want that to relate to the information in the details row.

    Allan
  • found it somewhere. sorry, don't remember where it was but here's the code to load an external url into a tab.

    replace the:

    oTable.fnOpen(nTr, fnFormatDetails(oTable, nTr), 'details');
    with this:

    $.get("/ajax/load_user.php?user_id=" + aData[1], function (response) {
    oTable.fnOpen(nTr, response.details, 'details');
    });

    and I return a JSON encoded array from /ajax/load_user.php?user_id=" + aData[1] using php like this:

    $data = array();
    $data['details'] = "<h1>hello world</h1>";

    return json_encode($data);

    Hope this helps!
  • Hi!

    I used the original example to create the details row. It contains a datatable with server side processing using JSON data. Everything is fine. I can use the add new data button and I customized the form to get all the data for the inner datatable. What I want to do when a new element added to the inner datatable is to refresh the original datatable to reflect the changes. I can refresh with the fnDraw(false) function inside the fnOnAdded event handler. What I cannot do is to leave the details row open. Not open again (although it would be a good workaround) but leave open.

    I tried to use
    		fnOnAdded: function(status)
    		{
    			oTable.fnDraw(false);
    			$(anOpen).each( function () {
    		        	if ( this == nTr ) {
               				$('td.control', this).click();
             			}
           			});
    			return true;
    		}
    
    but no luck.

    Any help welcome! Thanks!
  • I'm looking for help with this here if anyone can spare a few minutes please?

    http://www.datatables.net/forums/discussion/9255/help-with-drill-down-rows/p1

    Thanks
  • Hi,

    Can I use a nested data table as the original html from before intializing data tables?

    Something like:

    <table>
    <thead></thead>
    <tbody>
        <tr>
            <td>"parent" table</td>
        </tr>
        <tr>
            <td>
                <table>
                    <tr>
                        <td>nested table</td>
                    </tr>
                </table>
            </td>
        </tr>
    </tbody>
    </table>
    

    Thanks!
  • Yes - what you would need to do is run a Javascript function over your table before initialising DataTables, reading your details rows, attaching them to their parents as a property (so there is a relationship between then) and then removing the details rows from the DOM. Then initialise DataTables and when you need to show a details row, just put the row you previous removed back in :-)

    Allan
  • Allen,
    Thanks for great plug in. my issue is displaying multiple rows of json data in the drill down display.

    My initial table displays a roll up of funding for a contract [total amount added to it] I want the drill down to display each increment that was part of the total. Can you point me to any good examples?
    {with detailed explanation if possible?}

    I currently have it working displaying only the first row, How do I loop through the others?


    
    <script type="text/javascript">
    // the table containing the index data
    var oTable_2 = "";
    // the select row
    var nTargetRow = "";
    // 
    //
    var aData = ""; // holds the text for selected row (column position is critical)
    /* -----------------------------------------------------------------------
       Jquery Ajax action scrpts
       ----------------------------------------------------------------------- */
    // Method retrieves the selected row index id and executes the ajax call
    function getModDetails() 
    {   
    //alert('getModDetails');
    
    // retrieve the data from the selected row
        aData = oTable_2.fnGetData( nTargetRow );
    
        // build the url for the ajax call to use
    	var cfcUrl = '#application.ajax_url#';
    	    cfcUrl +='RemoteModDetails.cfc?';
            cfcUrl += 'method=getPos';
            cfcUrl +='&porderid=' + aData[1] ; 
    		
    		
        // make the ajax call
    	$.ajax(
    		 {
    			type: "GET",
                url: cfcUrl,
                cache: false,
                contentType: "application/json; charset=utf-8",
                data: "{}",
    			dataType: "json",
    			success: function (objResponse )
    				{	;
    				   if(objResponse.SUCCESS)
    					{   
    						openModDetailRow(objResponse.DATA);
    						
    					}
    					else
    					{
    						ShowErrors(objResponse.ERRORS);
    					}
    				},
    			error : function(jqXHR, textStatus, errorThrown ) {
    					ajaxErrorHandler( jqXHR, textStatus, errorThrown );
    				}
    		});
    }
    
    // Function ajaxErrorHandler
    //    DESCRITPION:  Error Handler fir the error message for failed ajax call'
    //    PARAMETERS:
    //      jqXHR       = The jqXHR (in jQuery 1.4.x, XMLHttpRequest) object,
    //      textStatus  = a string describing the type of error that occurred 
    //                    and an optional exception object, if one occurred. 
    //                    Possible values for the second argument (besides null) are 
    //                    "timeout", "error", "abort", and "parsererror". 
    //      errorThrown = the textual portion of the HTTP status, such as "Not Found" 
    //                    or "Internal Server Error."
    function ajaxErrorHandler( jqXHR, textStatus, errorThrown) 
    {
    	alert('Unable to retieve the requested information due to a server error');
    }
    // Handle the failed response error
    function ShowErrors( statusMsg)
    {
    	alert('Unable to retieve the requested information. Status: ' + statusMsg);
    } 
    // create the indormation row based on the data retruned by the ajax call
    function openModDetailRow(indexInfo)
    {
    	
    	/* Assumes only one row returns; additional rows ignored */
    	var aReqNumID         = indexInfo.DATA [0][0]; 
    	var aPorderID 	      = indexInfo.DATA [0][1]; 
    	var aRequistionNum    = indexInfo.DATA [0][2];
    	var aAwardDte 	      = indexInfo.DATA [0][3];
    	var  aAwardAmount     = indexInfo.DATA [0][4]; 
    	var aDteEntered	      = indexInfo.DATA [0][5]; 
    	var aEnteredBy 		  = indexInfo.DATA [0][6]; 
    	var aDteMod           = indexInfo.DATA [0][7]; 
    	var aModBy		      = indexInfo.DATA [0][8]; 
    	var aValidFlag	      = indexInfo.DATA [0][9];   
    	var aModNum		      = indexInfo.DATA [0][10]; 
    	var aDescription      = indexInfo.DATA [0][11]; 
    	
    	;
    	
    	// create the additonal information table
    	var sOut = '<table align="left" width="95%" cellpadding="2" cellspacing="0" border="1" class="hiddenTbl ui-widget-content">';
        // row 1
    	sOut += '<tr><td><strong>Mod Number:</strong><br> <strong style="color:##FF0;">&nbsp;'+ aModNum  +'</strong></td>';
    	sOut += '<td><strong>Requsition Numbeer:</strong><br> '+aRequistionNum + '</td><td><strong> Award Amount:</strong><br> <strong style="color:##FF0;">$' + aAwardAmount.toFixed(2) + '</strong></td></tr>';
        // row 2
    	sOut += '<tr><td ><strong>Award Date:</strong><br>'+ aAwardDte + ' </td><td ><strong>Description:</strong><br> ' + aDescription + '</td><td> </td></tr>';
        // end of table
    	sOut += '</table>';
    	
    	
    	// display the row
    	
    	oTable_2.fnOpen( nTargetRow, sOut, 'details' );
    
    }
    /* -----------------------------------------------------------------------
     * Jquery Display script
     * ----------------------------------------------------------------------- */
    $(document).ready(function()
    {
     	/*
    	 * Insert a 'details' column to the table
    	 */
    	var nCloneTh = document.createElement( 'th' );   // for header
    	var nCloneThF = document.createElement( 'th' );  // for footer
    	var nCloneTd = document.createElement( 'td' );   // for body
    	nCloneTd.innerHTML = '<img src="./images/details_open.png">';
    	nCloneTd.className = "center";
    	// add blank header
    	$('##MVW thead tr').each( function () {
    		this.insertBefore( nCloneTh, this.childNodes[0] );
    	} );
    	// add blank footer header
    	$('##MVW tfoot tr').each( function () {
    		this.insertBefore( nCloneThF, this.childNodes[0] );
    	} );
    	// add icon open/close info rowrow
    	$('##MVW tbody tr').each( function () {
    		this.insertBefore(  nCloneTd.cloneNode( true ), this.childNodes[0] );
    	} );
    	// run the jquery data table script
    		oTable_2 = $('##MVW');
    		oTable_2.dataTable({
    			"aoColumnDefs": 
    				[ { "bSortable": false, "aTargets": [ 0 ] } ],
    			"aaSorting": [[1, 'asc']],
    			"iDisplayLength": 5,
    			"aLengthMenu": [[5,10, 25, 50, 100, -1], [5,10, 25, 50, 100, "All"]],
    			"bJQueryUI": true
    			});/* end data table */
    			
    	/* Add event listener for opening and closing details
    	 * Note that the indicator for showing which row is open is not controlled by DataTables,
    	 * rather it is done here
    	 */
    	$('##MVW tbody td img').live('click', function () {
    		nTargetRow = this.parentNode.parentNode;
    		if ( this.src.match('details_close') )
    		{	
    			/* This row is already open - close it */
    			this.src = "./images/details_open.png";
    			oTable_2.fnClose( nTargetRow );
    		}
    		else
    		{
    			
    			this.src = "./images/details_close.png";
    			getModDetails();
    		}
    	} );
    			
    	}); 
    	/* end ready */
    </script> 
    
    
    

    thanks.
  • Thanks for great plugin Allen. I need to implement a drill down data table within the drill down data table. Hope you understood my question. Is that possible to expand the rows in inner table ? Thanks.
  • Is that possible to expand the rows in inner table ?

    Yes it is - but you need to actually make the inner table 100% independent from the parent table. For example, at the moment the columns would not align between the parent and child. If that is fine then you just need to put your table into the 'details' row and initialise it like any other table.

    It is actually possible to get the columns to align 100%, but it isn't trivial at the moment and involves a bit of work.

    Allan
  • Allen,
    Do you know if anyone has successfully done a "expand/collapse all" for Drill-Downs?
    I've tried, and while it opens/expands just fine, it doesn't close/collapse past the first entry.
    Most likely my code on how I'm doing it, below is the code for it.

    	$("#expandAllTR").click(function (){
    		$("#parentTable tbody tr").each(function() {
    			if (oTable.fnIsOpen(this)){
    				oTable.fnClose(this);
    			}else{
    				var position = oTable.fnGetPosition(this);
    				var info = oTable.fnGetData(position)[11];
    				oTable.fnOpen( this, info, "info_row" );
    			}
    		});
    		if($("#expandAllTR").html()=="Expand All"){
    			$("#expandAllTR").html("Collapse All");
    		}else{
    			$("#expandAllTR").html("Expand All");
    		}
    	});
    
  • Hi,

    That looks really close to me - the only problem I can see with the code is that your loop will encompass all TR elements in the table - not just those which are 'open rows' (i.e. it is calling fnClose on the details row as well).

    This can be addressed by using the $ API method to get all TR elements under the table's control. Do do that, change:

    $("#parentTable tbody tr").each(function() {
    

    to:

    oTable.$('tr').each(function() {
    

    This is the result: http://live.datatables.net/eruhof/edit#javascript,html .

    Regards,
    Allan
  • Hi Allan,

    I am showing one row details at a time. Is there any way to pass DOM element (not string) as the content of the details.
    I have a hidden form in my page which I want to append (not clone) as the 'innerDetails'.
  • Hi Allan,

    Thanks for sharing the drill down rows example. I need help in implementing some specific requirement for drill down rows.

    I am getting all the parent-child data from server in one go (no ajax needed). Each row has an column indicator which has 'p' for parent and 'c' for children and they come in proper order like..

    + aaa, bbbb, cccc, dddd, eeee, ffff, p
    - aaa, bbb1, ccc1, ddd1, eeee1, ffff1, c
    - aaa, bbb2, ccc2, ddd2, eeee2, ffff2, c
    aaa, bbbb, cccc, dddd, eeee, ffff, p
    + aaa, bbbb, cccc, dddd, eeee, ffff, p
    + aaa, bbbb, cccc, dddd, eeee, ffff, p
    If a parent has children then only the expand/collapse should appear else it will be normal row.

    Please if you could help me with some pointers regarding the same.

    Thanks in anticipation,
    Kamlesh
  • Is there anyway to keep the expanded row after calling a fnDraw to refresh the JSON data?
  • I'm trying to get expandable rows on my dataTable that uses a google spreadsheet as it's data source. I used tabletop.js to pull the data and push it to datatables, but I have to use 'sTitle' in order for dataTables to read my spreadsheet correctly.

    I need to use mdataProp, to get the expandable row working, correct? Wondering if anyone has any solutions. Thanks!

    Code: http://media.hamptonroads.com/media/content/pilotonline/Datatables/football/footballRecruits.html
  • @jonnyd55: If you want to read object properties for your columns - then yes, mDataProp is the way forward: http://datatables.net/blog/Extended_data_source_options_with_DataTables

    Allan
  • I ended up solving this by closing the rows on footer callback - so all the data has already been repopulated into the table..

    If you try to use other callbacks, not enough of the state has been initialized yet to call fnClose...

        fnFooterCallback: function( nFoot, aData, iStart, iEnd, aiDisplay ) {
          $(oTable).find("tr").each(function() {
    	      if (oTable.fnIsOpen(this)) {
    		       oTable.fnClose(this);
    	      }
          });
        }
    
  • Hi Allan, Thanks for providing this excellent plugins, I have use this datatables many time, but one thing that I can't figure out is, when I'm using Drill down then I try to make the drill down content also use the datatables, the tables is display correctly but all the datatables feature is not working, such as filtering, sorting, paging, all feature is not working (in drill down content that I apply the datatables on it), is there any function or solution to fix this, thanks for helping me
  • I have an accordion ul and in each section of the accordion I'm using a drilldown table. The drilldown table is created when I click the header to expand out a section of the accordion.

    If I view a table in one category , then move to another category, then come back to the original category, I can no longer drill down. I get an error: 'aoData' is null or not an object when I click the + button.

    What am I missing?

    I've replaced the static '#example' with my variable name.

    function initTable(category)
    {
    getPats(category);
    var aDataSet = getDataSet();
    var anOpen = [];
    var sImageUrl = "../images/";

    /*var tbl = document.getElementById(category);
    if (tbl)
    tbl.parentNode.removeChild(tbl);
    */

    var oTable = $('#'+category).dataTable( {
    "bProcessing": false,
    "bDestroy": true,
    "aaData": aDataSet,
    "aoColumns": [
    {
    "mDataProp": null,
    "sClass": "control center",
    "sDefaultContent": '<img src="'+sImageUrl+'details_open.png'+'">'
    },
    { "mDataProp": "patname" },
    { "mDataProp": "age" },
    { "mDataProp": "fin" },
    { "mDataProp": "roombed" }
    ]
    } );

    $('#'+category+' td.control').live( 'click', function ()
    {
    var nTr = this.parentNode;
    var i = $.inArray( nTr, anOpen );

    if ( i === -1 )
    {
    $('img', this).attr( 'src', sImageUrl+"details_close.png" );
    var nDetailsRow = oTable.fnOpen( nTr, fnFormatDetails(oTable, nTr), 'details' );
    $('div.innerDetails', nDetailsRow).slideDown();
    anOpen.push( nTr );
    }
    else
    {
    $('img', this).attr( 'src', sImageUrl+"details_open.png" );
    $('div.innerDetails', $(nTr).next()[0]).slideUp( function ()
    {
    oTable.fnClose( nTr );
    anOpen.splice( i, 1 );
    } );
    }
    } );
    }

    Thanks in advance
  • I'm new to Datatables, jQuery, and MVC and am trying to model your example for drill-down tables. Naturally I wouldn't be trying something easy first! :) The Ajac call is working, and I've got the master rows of the table rendering but can't get the .live event to fire. I tried putting an alert in there and it doesn't do anything. I think I may be missing some CSS and can't find anything on it. Perhaps I'm barking up the wrong tree? I think my code is close but can't quite get there. Also, I get an uncaught error trying to use .live so I switched it to .on after seeing that .live is deprecated. This is an ASP.Net project

    I would be grateful for any pointers you can give me, particularly on how .live works... is that jQuery or DataTables? I'm stumped at the moment.

    Here is my code:

    //_Layout.vbhtml defines includes
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>@ViewData("Title")</title>
        <link href="@Url.Content("~/Content/DataTables-1.9.4/media/css/jquery.dataTables.css")" rel="stylesheet" type="text/css" />
        <!--<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />-->
        <link href="@Url.Content("~/Content/DataTables-1.9.4/media/css/demo_table.css")" rel="stylesheet" type="text/css" />
    
    
        <script src="@Url.Content("~/Scripts/jquery-1.9.0.min.js")" type="text/javascript"></script>
        <script src="@Url.Content("~/Scripts/modernizr-2.6.2.js")" type="text/javascript"></script>
        <script src="@Url.Content("~/Scripts/DataTables-1.9.4/media/js/jquery.dataTables.js")" type="text/javascript"></script>
    </head>
    <body>
        @RenderBody()
    </body>
    </html>
    

    //view page
    @ModelType MVC3Test.BatchCollection
    
    @Code
        ViewData("Title") = "Batchlist2"
    End Code
    
    <h2>Batchlist2</h2>
    
    <table id="batch_table" class="display">
        <thead>
            <tr>
                <th></th>
                <th>BatchID</th>
                <th>Transmitted Date/Time</th>
                <th>Completed Date/Time</th>
                <th>Created By</th>
                <th>Created Date/Time</th>
            </tr>
        </thead>
        <tbody>
        </tbody>
    </table>
    
    <script type="text/javascript" language="javascript">
    
        // This will run the code instead the function() {} as soon as the page is ready.
        // Hence: Document.ready
        $(document).ready(function () {
            var anOpen = [];
            var oTable;
            var sImageURL = "/Images/"; //path to image files
    
            // AJAX call to get batch list
            // it will return into the data object
            $.ajax(
            {
                type: "GET",
                url: '@Url.Action("FetchBatchList", "Batch")',
    
                error: function (xhr, statusText, errorThrown) {
                    if (statusText === 'timeout')
                        alert('The server is not responding');
                    if (statusText === 'error')
                        alert("Error: " + errorThrown);
                },  //end of ajax error block
    
                success: function (data) {
    
                //"sAjaxSource": "",
                    // Create Data Table
                    oTable = $('#batch_table').dataTable(
                                    {
                                        "bProcessing": true,
                                        "aaData": data.BatchList, // Initialize datatables with data object
                                        "aoColumns": [  // Bind table columns with properties
                                                        {
                                                            "mData": null,
                                                            "sClass": "control center",
                                                            "sDefaultContent": '<img src="' + sImageURL + 'details_open.png' + '">'
                                                        },
                                                        { "mData": "BatchID" },
                                                        { "mData": "Transmitted_DateTime" },
                                                        { "mData": "Completed_DateTime" },
                                                        { "mData": "Created_EmpID" },
                                                        { "mData": "Created_DTTM" }
                                                     ]
                                                    }); // End dataTable()
                                    //debugger;
                } //end of ajax success block
            }); // end $.ajax call
        });  // end $(document).ready(....
    
    
        // animation control code
        $('#batch_table td.control').on('click', function () {  //.live is deprecated in jQuery 1.7... use .on instead
            alert('Hello from animation control code');
            var nTr = this.parentNode;
            var i = $.inArray(nTr, anOpen);
            debugger;
    
            if (i === -1) {
                $('img', this).attr('src', sImageURL + "details_close.png");
                var nDetailsRow = oTable.fnOpen(nTr, fnFormatDetails(oTable, nTr), 'details');
                $('div.innerDetails', nDetailsRow).slidedown();
                anOpen.push(nTr);
            }
            else {
                $('img', this).attr('src', sImageURL + "details_open.png");
                $('div.innerDetails', $(nTr).next()[0]).slideup(function () {
                    oTable.fnClose(nTr);
                    anOpen.splice(i, 1);
                });
            }
        });
    
        // detail table row html goes in the following section
        function fnFormatDetails(oTable, nTr) {
            var oData = oTable.fnGetData(nTr);
            debugger;
            var tranData;   //this will contain an instance of a transaction object from the transactions collection
            for (tranData in oData.TranCollection) {
                var sOut = '<div class="innerDetails">' +
                                '<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">' +
                                    '<tr>' +
                                        '<td>TransactionID:</td><td>' + tranData.ID + '</td>' +
                                        '<td>SOR_ID:</td><td>' + tranData.SystemOfRecordID + '</td>' +
                                        '<td>PostingFlag:</td><td>' + tranData.PostingFlag + '</td>' +
                                        '<td>Posting_DTTM:</td><td>' + tranData.Posting_DTTM + '</td>' +
                                        '<td>PostingMessage:</td><td>' + tranData.PostingMessage + '</td>' +
                                        '<td>PostingMessage:</td><td>' + tranData.XMLTransactionData + '</td>' +
                                    '</tr>' +
                                '</table>' +
                           '</div>'; 
                return sOut;
            }
        }
    
    </script>
    
    
    
  • @Rascal
    When using .on to replace .live the syntax changes. Your line

    $('#batch_table td.control').live('click', function () {

    would become

    $(document).on('click','#batch_table td.control', function () {
  • i am lost on the dynamic way the table is built.

    http://www.roxstyle.com/roxprojects/blssi/studiosystem-v4-2013/ssv4/html-nbcu/nbc-pitchgrid.html

    1. i can not get the oData to show in my sample using the same code as the site sample. So i have the initial table structure in the markup.
    2. once i have clicked the control button, i get an error
    "Cannnot read property of 'aoData' of null"

    with a reference to the following line
    oTable.fnOpen( nTr, fnFormatDetails(oTable, nTr) , 'details' );

    i have an array like the sample

    can someone give me a direction of where to look to fix this? is it a problem with the array? the markup? or something i am just unaware of?

    i understand how to .append a row with jquery, but i don't know if that will work with datatables.
  • hello everyone, first time here so be gentle.
    I want to thank Alan and everyone for the great examples and incite posted here. I have implement drill down tables successfully with one exception.
    I implemented a datatable on the drill down table and it's working fine in all browser except IE.
    I get the table but the datatable was not applied.

    If anyone has any incite or sugestion I would greatly appreciate it.
  • Just wanted to add something to this, it also works in IE9 and above. It is only an issue with IE8/7
  • allan,
    Unfortunately I am unable to expose the site to the internet right now. I have include the drill down table code. Please let me know how else I can help in resolving this. I will work on posting a test case to live.datatables.net

    Thanks
    David

        	    $('#basetable td.control').live( 'click', function () {
        	    	   var nTr = this.parentNode;
        	    	   var i = $.inArray( nTr, anOpen );
        	    	    
        	    	   if ( i === -1 ) {
        	    	      $('img', this).attr( 'src', "/mf/forward/images/details_close.png" );
        	    	      baseTable.fnOpen( nTr, fnFormatDetails(baseTable, nTr), 'details' );
        	    	      anOpen.push( nTr );
        	    	    }
        	    	    else {
        	    	      $('img', this).attr( 'src', "/mf/forward/images/details_open.png" );
        	    	      baseTable.fnClose( nTr );
        	    	      anOpen.splice( i, 1 );
        	    	    }
        	    	} );
        	    	 
        	    	function fnFormatDetails( oTable, nTr )
        	    	{
            	    	//alert(nTr);
        	    	  var oData = oTable.fnGetData( nTr );
        	    	  //alert(oData[11]);
        	    	  var fundNum = oData[11].trim();
        	    	  var sOut =
        	    	    '<div class="innerDetails">'+
        	    	    '<script type="text/javascript">' +
        	    	    '$("#tableDetails'+fundNum.trim()+'").dataTable( {'+
        		    	'"bFilter": false,'+
        		    	'"bPaginate": false,'+
        		    	'"bInfo": false,'+
        		    	'"bAutoWidth": false,'+				    
        			    '"oLanguage": {'+
        			    '    "sEmptyTable": "No transactions were found. Please modify your criteria and search again."'+
            	    	'},'+
            	    	'"aoColumns": ['+
            	    	'	{ "sTitle": "Trade Date",'+
                	    '	  "sClass": "left" },'+
            	    	'	{ "sTitle": "Description",'+
                  	    '	  "sClass": "right" },'+
            	    	'	{ "sTitle": "Price",'+
                  	    '	  "sClass": "right" },'+
            	    	'	{ "sTitle": "Shares",'+
                  	    '	  "sClass": "right" },'+
            	    	'	{ "sTitle": "Amount",'+
                  	    '	  "sClass": "right" }'+
            	    	']'+
        	    	    '});' +
        	    	    '</' +
        	    	    'script>' +
        	    	      '<table id="tableDetails'+fundNum.trim()+'" class="display" cellpadding="0" cellspacing="0" border="0">'+
        	    	      	'<tbody>';
        	    	     if(detailsTableData.length > 0 )
        	    	     { 
            	    	     for(var i=0;i<detailsTableData.length;i++)
            	    	     { 
                	    	     if(detailsTableData[i][0] ===  fundNum )
                	    	     {         
        	    	    	 		sOut +='<tr>'+
        	    	        		'<td>'+detailsTableData[i][1]+'</td>'+
        	    	        		'<td>'+detailsTableData[i][2]+'</td>'+
        	    	        		'<td>'+detailsTableData[i][3]+'</td>'+
        	    	        		'<td>'+detailsTableData[i][4]+'</td>'+
        	    	        		'<td>'+detailsTableData[i][5]+'</td>'+
        	    	        		'</tr>';
                	    	     }
            	    	     }
            	    	    sOut += '</tbody><tfoot>' +
            	    	    		'<tr>' +
    		    					'<th></th>' +
    		      					'<th></th>' +
    		      					'<th></th>' +
    		      					'<th></th>' +
    		      					'<th></th>' +
    		    					'</tr>' +
    		  						'</tfoot>'; 
        	    	     	sOut += '</table>'+
        	    	    		 '</div>';
        	    	     }  
        	    	  return sOut;
        	    	}
    
  • alan,

    I believe I have resolved my problem. I have moved the datatable instantiation to the control function from the fnFormatDetails function.
    Thanks for the great JavaScript solution.
  • I have the drilldown working nearly perfect with the only change in your sample being updating the trigger to on for jQuery 1.9.x. The slideup annimation works perfectly, however the slidedown animation does not work at all. My details just pop into place, no slide at all.

    How can I troubleshoot this?

    	        $(document).on("click", "#classifiedAds td.control", function () {
    	            var nTr = this.parentNode;
    	            var i = $.inArray(nTr, anOpen);
    
    	            if (i === -1) {
    	                $('img', this).attr('src', "/images/details_close.png");
    	                var nDetailsRow = oTable.fnOpen(nTr, fnFormatDetails(oTable, nTr), 'details');
    	                $('div.innerDetails', nDetailsRow).slideDown('slow');
    	                anOpen.push(nTr);
    	            }
    	            else {
    	                $('img', this).attr('src', "/images/details_open.png");
    	                $('div.innerDetails', $(nTr).next()[0]).slideUp(function () {
    	                    oTable.fnClose(nTr);
    	                    anOpen.splice(i, 1);
    	                });
    	            }
    	        });
    
    
  • Ignore, figured out my second question. However, my first one is still unresolved.
  • Is it possible to create nested datatables with the features such as Column Reordering and column resizing , in such a way that if columns of external datatable are reordered or resized then the same effects should be applied to the inner datatable, considering the column headers in the nested table to be exactly same as the outer table..?
  • Currently no - there is no easy way of doing that - sorry. You could probably write a script to keep multiple DataTables in sync, but there isn't existing code for that.

    Allan
  • Hi, sorry I'm kinda newby with jquery datagrid. I've found your post and the whole Datatables.net script very interesting.
    I'm developing a web application that requires most of these features. Particularly in this example ( slidedown-slideup) it's not clear to me where the div with class .innerDetails is placed in the DOM..the original example worked fine for me:

    <code> $('#example tbody td img').live('click', function () {
    var nTr = $(this).parents('tr')[0];
    if ( oTable.fnIsOpen(nTr) )
    {
    /* This row is already open - close it */
    this.src = "../examples_support/details_open.png";
    oTable.fnClose( nTr );
    }
    else
    {
    /* Open this row */
    this.src = "../examples_support/details_close.png";
    oTable.fnOpen( nTr, fnFormatDetails(oTable, nTr), 'details' );
    }
    } ); </code>
    but not this one with slideup-down functions which is the one I need..
    thank for your time! best regards
  • Hey guys,

    I just use this dataTable option and got a problem using Jquery Live function (removed since 1.9).

    This thread need an update, use .click function or .on function !

    Guillaume

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Support

Get useful and friendly help straight from the source.