Table format breaks when entries length changes and hidden row details are enabled

Table format breaks when entries length changes and hidden row details are enabled

rs217000rs217000 Posts: 8Questions: 0Answers: 0
edited December 2013 in General
Hi,

I've been trying to get the "hidden row details" feature working in my table (using this as an example: http://www.datatables.net/examples/api/row_details.html), which uses a Javascript array for data.

Everything looks great on the initial load, but when "Show X Entries" is changed, everything below the first 10 lines (10 is my default entry length), shifts over to the "details" column, which fails to load the details for the subsequent rows and messes up the formatting. I suspect my issue lies in my "insert a details column to the table" section, but my efforts to pinpoint it have failed. I've included the bulk of my code below; please show this noob the error of his ways:

[code]

function fnFormatDetails ( oTable, nTr )
{
var aData = oTable.fnGetData( nTr );
var sOut = '';
sOut += 'Rendering engine:'+aData[1]+'';
sOut += 'Link to source:link here';
sOut += 'Extra info:And any further details here (images etc)';
sOut += '';

return sOut;
}

/*
* Initialse DataTables, with no sorting on the 'details' column
*/
$(document).ready(function() {

$('#dynamic').html( '' );


var oTable = $('#example').dataTable( {
"sDom": 'C<"clear">lfrtip<"clear">',
/*"iDisplayLength": 5,*/
"aaData": guns,
"aaSorting": [[25,'desc']],
"aoColumnDefs": [
{ "bSortable": false, "aTargets": [ 0 ] },
{
"aTargets": [10,11],
"fnRender": function ( o ) {
return "$"+o.aData[ o.iDataColumn ];
}
},

{
"aTargets": [9],
"fnRender": function ( o ) {
return o.aData[ o.iDataColumn ]+" oz.";
}
},

{
"aTargets": [6,7,8],
"fnRender": function ( o ) {
return o.aData[ o.iDataColumn ]+'"';
},
}

],

"aoColumns": [
{ "sTitle": "Model" },
{ "sTitle": "Make" },
{ "sTitle": "Type" },
{ "sTitle": "Caliber" },
{ "sTitle": "Trigger" },
{ "sTitle": "Capacity" },
{ "sTitle": "Length" },
{ "sTitle": "Barrel Length" },
{ "sTitle": "Height" },
{ "sTitle": "Weight" },
{ "sTitle": "MSRP" },
{ "sTitle": "Used Price"/*, "bVisible": false */},
{ "sTitle": "Recoil %", "bVisible": false },
{ "sTitle": "Power %", "bVisible": false },
{ "sTitle": "Capacity %", "bVisible": false },
{ "sTitle": "Affordability %", "bVisible": false },
{ "sTitle": "Concealability %", "bVisible": false },
{ "sTitle": "Safety", "bVisible": false },
{ "sTitle": "Ambi Features", "bVisible": false },
{ "sTitle": "Adj Backstrap", "bVisible": false },
{ "sTitle": "Small Hands", "bVisible": false },
{ "sTitle": "Weak Hands", "bVisible": false },
{ "sTitle": "Ambi Safe", "bVisible": false },
{ "sTitle": "Poly Frame", "bVisible": false },
{ "sTitle": "Acc Rail", "bVisible": false },
{ "sTitle": "Overall" }
]

} );

/*
* Insert a 'details' column to the table
*/
var nCloneTh = document.createElement( 'th' );
var nCloneTd = document.createElement( 'td' );
nCloneTd.innerHTML = '';
nCloneTd.className = "center";

$('#example thead tr').each( function () {
this.insertBefore( nCloneTh, this.childNodes[0] );
} );

$('#example tbody tr').each( function () {
this.insertBefore( nCloneTd.cloneNode( true ), this.childNodes[0] );
} );

/* 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
*/
$('#example tbody td img').live('click', function () {
var nTr = this.parentNode.parentNode;
if ( this.src.match('details_close') )
{
/* This row is already open - close it */
this.src = "http://datatables.net/examples/examples_support/details_open.png";
oTable.fnClose( nTr );
}
else
{
/* Open this row */
this.src = "http://datatables.net/examples/examples_support/details_close.png";
oTable.fnOpen( nTr, fnFormatDetails(oTable, nTr), 'details' );
}
} );

} );

[/code]


Thanks!

Replies

  • allanallan Posts: 63,133Questions: 1Answers: 10,399 Site admin
    Inject the additional column _before_ you initialise the DataTable, like in the example. Otherwise it is injecting the data into the DOM displayed data only - i.e. the first page.

    Allan
  • rs217000rs217000 Posts: 8Questions: 0Answers: 0
    Hey Allan,

    Thanks for the quick feedback, and of course, the excellent plug-in.

    I implemented your suggestion, but it loads without the details column altogether. I may be mistaken, but I believe the reason the details column doesn't exist when I place the "insert details" code before the initialization is because there's nothing in the table at the time. My table is created using a Javascript Array (I pretty much copied your method here: http://www.datatables.net/release-datatables/examples/data_sources/js_array.html).

    ...does that diagnosis make sense? If so, is there anyway to add the "show details" column using the method show in the said example?

    Please forgive my ignorance here--again, I sincerely appreciate your assistance.
  • allanallan Posts: 63,133Questions: 1Answers: 10,399 Site admin
    > My table is created using a Javascript Array (I pretty much copied your method

    Ah - I hadn't realised that. In which case, you don't want to be manipulating the DOM by cloning elements etc. Use a technique like this one: http://datatables.net/release-datatables/examples/server_side/row_details.html

    Allan
  • rs217000rs217000 Posts: 8Questions: 0Answers: 0
    Allan, I appreciate the guidance. I'll follow your lead from that link and report back with some sample code once I reach the resolution.


    Thanks again!
  • rs217000rs217000 Posts: 8Questions: 0Answers: 0
    edited December 2013
    Hey Allan,

    I read into the server-side processing example that you offered; that's a direction I'd like to take my project in its next iteration, but right now, I don't posses the chops to make it happen...plus, there are some other functions on the page that require things to be presented in their current configuration.

    That said, I'd like to explore a different direction in hopefully achieving the same result. I've been looking into the callback functions you created, and "fnDrawCallback" might help me achieve what I'm looking for.

    What I'm playing with is making the "add details" code a function, and calling it every time a change is made to the DOM (e.g., number of shown entries changes). What I'm struggling with using this method is getting the function to properly evaluate whether or not the row already contains a "details" column. I'm thinking evaluating the number of columns in a row may help determine whether the details have already been added

    I'm going for something like this:

    Note: I've been trying to add code here, but it keeps cutting off...I'm going to try to continue this post in a new post below...
  • rs217000rs217000 Posts: 8Questions: 0Answers: 0
    [code]
    var addDetails = function(){
    /*
    * Insert a 'details' column to the table
    */
    var nCloneTh = document.createElement( 'th' );
    var nCloneTd = document.createElement( 'td' );
    nCloneTd.innerHTML = '';
    nCloneTd.className = "center";

    $('#example thead tr').each( function () {
    /*if length of column in row < the number of columns with details added){*/
    this.insertBefore( nCloneTh, this.childNodes[0] );
    /*} */
    } );

    $('#example tbody tr').each( function () {
    /*if length of columns in row < the number of columns with details added){*/
    this.insertBefore( nCloneTd.cloneNode( true ), this.childNodes[0] );
    /*
    }
    */
    } );
    }

    [/code]

    Then I initialize the table with the following:

    [code]
    $(document).ready(function() {


    $('#dynamic').html( '' );

    var oTable = $('#example').dataTable( {
    "fnDrawCallback": function () {
    addDetails();
    },
    "sDom": 'C<"clear">lfrtip<"clear">',
    /*"iDisplayLength": 25,*/
    "aaData": guns,
    "aaSorting": [[25,'desc']],
    "aoColumnDefs": [
    { "bSortable": false, "aTargets": [ 0 ] },
    {
    "aTargets": [10,11],
    "fnRender": function ( o ) {
    return "$"+o.aData[ o.iDataColumn ];
    }
    },

    {
    "aTargets": [9],
    "fnRender": function ( o ) {
    return o.aData[ o.iDataColumn ]+" oz.";
    }
    },

    {
    "aTargets": [6,7,8],
    "fnRender": function ( o ) {
    return o.aData[ o.iDataColumn ]+'"';
    },
    }
    [/code]

    If the answer isn't too much trouble, how might I go about evaluating each row for column length (and subsequently inserting the details if it needs them) in my function?

    Thanks again!
  • allanallan Posts: 63,133Questions: 1Answers: 10,399 Site admin
    I would very much recommend not manipulating the DOM using fnRowCallback to add row details. There are much clearer ways of doing it.

    The best way I can suggest at the moment uses the upcoming DataTables 1.10: https://github.com/DataTables/DataTables/blob/master/examples/server_side/row_details.html#L29 . Although that is server-side processing, the same principle applies just as well to client-side processing with Ajax loaded data, or Javascript array sourced data.

    Allan
  • rs217000rs217000 Posts: 8Questions: 0Answers: 0
    edited December 2013
    Hey Allan,

    Thanks for all your assistance. I'll be trying your posted method (perhaps from a live DB, rather than a js array) in the next revision of the app I'm working on. For the time being, however, I decided to take the route of using Datatables from the DOM (I realize performance and compatibility won't be as great, but it's getting the job done so far).

    I'm lazy, and wanted to create the table from my existing js array (plus, I need the table to update from the array at times), so I put together a function that builds the table in the DOM. At this point, I'm able to add row details properly and initialize the datatables plugin using the the features I was using previously (again, I was having a problem getting the details to insert properly after building my table using the method in this example: http://www.datatables.net/release-datatables/examples/data_sources/js_array.html).

    In case anyone is having a similar issue, here's the code I'm using to build the table from a js array, insert details, and initialize the datatables plugin:

    Div and table elements:

    [code]

    [/code]

    The rest:

    [code]
    function fnFormatDetails ( oTable, nTr )
    {
    var aData = oTable.fnGetData( nTr );
    var sOut = '';
    sOut += 'Rendering engine:test';
    sOut += 'Link to source:link here';
    sOut += 'Extra info:And any further details here (images etc)';
    sOut += '';

    return sOut;
    }

    $(document).ready(function() {
    var makeTable = function() {
    var $table = $(document.getElementById("example"));
    //$($table).attr("class","hide");
    var $headLine = $("ModelMakeTypeCaliberTriggerCapacityLengthBarrel LengthHeightWeightMSRPUsed PriceRecoil %Power %Capacity %Affordability %Concealability %SafetyAmbi FeaturesAdj BackstrapSmall HandsWeak HandsAmbi SafePoly FrameAcc RailOverall"); //creates the header
    $("#example > thead").append($headLine);

    for (var i = 0; i < guns.length; i++) {
    var gun = guns[i];
    var $line = $( "" );
    $line.append( $( "" ).html( gun[0] ) );//Model
    $line.append( $( "" ).html( gun[1] ) );//Manufacturer
    $line.append( $( "" ).html( gun[2] ) );//Type
    $line.append( $( "" ).html( gun[3] ) );//Caliber
    $line.append( $( "" ).html( gun[4] ) );//Trigger
    $line.append( $( "" ).html( gun[5] ) );//Capacity
    $line.append( $( "" ).html( gun[6] ) );//Length
    $line.append( $( "" ).html( gun[7] ) );//Barrel Length
    $line.append( $( "" ).html( gun[8] ) );//Height
    $line.append( $( "" ).html( gun[9] ) );//Weight
    $line.append( $( "" ).html( gun[10] ) );//MSRP
    $line.append( $( "" ).html( gun[11] ) );//Used Est
    $line.append( $( "" ).html( gun[12] ) );//Recoil %
    $line.append( $( "" ).html( gun[13] ) );//Power %
    $line.append( $( "" ).html( gun[14] ) );//Capacity %
    $line.append( $( "" ).html( gun[15] ) );//Price %
    $line.append( $( "" ).html( gun[16] ) );//Concealability %
    $line.append( $( "" ).html( gun[17] ) );//Safety
    $line.append( $( "" ).html( gun[18] ) );//Ambi Other
    $line.append( $( "" ).html( gun[19] ) );//Adj Backstrap
    $line.append( $( "" ).html( gun[20] ) );//Small Hands
    $line.append( $( "" ).html( gun[21] ) );//Weak Hands
    $line.append( $( "" ).html( gun[22] ) );//Ambi Safe
    $line.append( $( "" ).html( gun[23] ) );//Polymer
    $line.append( $( "" ).html( gun[24] ) );//Rail
    $line.append( $( "" ).html( gun[25] ) );//OVERALL

    $("#example > tbody").append( $line );

    /*$("#example > tbody").append( $line );*/
    }
    $table.appendTo(document.body);
    };

    makeTable();


    /*
    * Insert a 'details' column to the table
    */

    var nCloneTh = document.createElement( 'th' );
    var nCloneTd = document.createElement( 'td' );
    nCloneTd.innerHTML = '';
    nCloneTd.className = "center";

    $('#example thead tr').each( function () {
    this.insertBefore( nCloneTh, this.childNodes[0] );
    } );

    $('#example tbody tr').each( function () {
    this.insertBefore( nCloneTd.cloneNode( true ), this.childNodes[0] );
    } );


    var oTable = $('#example').dataTable({
    "sDom": 'C<"clear">lfrtip<"clear">',
    "aaSorting": [[25,'desc']],
    "aoColumnDefs": [
    { "bSortable": false, "aTargets": [ 0 ] },
    {
    "aTargets": [10,11],
    "fnRender": function ( o ) {
    return "$"+o.aData[ o.iDataColumn ];
    }
    },

    {
    "aTargets": [9],
    "fnRender": function ( o ) {
    return o.aData[ o.iDataColumn ]+" oz.";
    }
    },

    {
    "aTargets": [6,7,8],
    "fnRender": function ( o ) {
    return o.aData[ o.iDataColumn ]+'"';
    },
    }

    ]
    });


    /* 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
    */
    $('#example tbody td img').live('click', function () {
    var nTr = this.parentNode.parentNode;
    if ( this.src.match('details_close') )
    {
    /* This row is already open - close it */
    this.src = "http://datatables.net/examples/examples_support/details_open.png";
    oTable.fnClose( nTr );
    }
    else
    {
    /* Open this row */
    this.src = "http://datatables.net/examples/examples_support/details_close.png";
    oTable.fnOpen( nTr, fnFormatDetails(oTable, nTr), 'details' );
    }
    } );

    });

    [/code]

    My array looks something like this:

    [code]
    var guns =
    [
    ["Beretta Model Stampede Deluxe.","Beretta","Revolver",".45 Colt","Single Action (SA)",6,11,"5.5","No Data",37.6,695,465,"23","99","0","42","3","n","n","n","n","n","n","n","n",0,9.78,218809],
    ["Beretta Model Stampede Bisley.","Beretta","Revolver",".45 Colt","Single Action (SA)",6,10.55,"5.5","No Data",38.4,650,425,"27","99","0","48","3","n","n","n","n","n","n","n","n",0,9.58,218809],
    ["Beretta Model Stampede Deluxe.","Beretta","Revolver",".357 Mag","Single Action (SA)",6,11,"5.5","No Data",37.6,695,465,"17","99","0","42","3","n","n","n","n","n","n","n","n",0,10.11,201737]
    ]

    [/code]

    Hope that helps someone.

    Thanks again, Allan!
This discussion has been closed.