fnDrawCallback

fnDrawCallback

zygimantaszygimantas Posts: 33Questions: 0Answers: 0
edited March 2009 in General
What is the point of fnDrawCallback function, when it takes no parameters and returns void?
The documentation says: "This function is called on every 'draw' event, and allows you to dynamically modify any aspect you want about the created DOM".

How can we access oTable inside this callback? Am I missing something here?

Replies

  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin
    The point of this callback was to allow you to alter the DOM that has just been displayed by DataTables (the draw is complete, and perhaps you want to modify one of the displayed cells in some manner). It's main use was actually back in the days of DataTables 1.3 (and before) where events applied to elements in the table were lost on every draw, so you would have to reapply them each time. So to some extent it isn't needed any more - although having said that there are perhaps times when it will be useful to know when the table has been re-drawn.

    You are right, it doesn't take any parameters or return anything (what would it return, given that the table has already been drawn). Perhaps it would be an idea to pass the settings object, and I'll include this in 1.5.

    Allan
  • zygimantaszygimantas Posts: 33Questions: 0Answers: 0
    well, so I have a situation: I'd like to call a plugin (fnGetColumnData) from fnDrawCallback function. I guess it is not possible now, right?
  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin
    Can't you just do: oTable = $('#whatever').dataTable( { fnDrawCallback: function() { oTable.fnGetColumnData();... } } ); ?

    The only trick there might be that the oTable object isn't fully initialised when you then want to call it... Sorry it's not tested. I'll have a further look tomorrow.
  • zygimantaszygimantas Posts: 33Questions: 0Answers: 0
    edited March 2009
    Yes, it's not initialized yet... Thanks in advance.
  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin
    Hi,

    Okay, there are two possibilities for you:

    [code]
    $(document).ready(function() {
    oTable = $('#example').dataTable( {
    "fnDrawCallback": function () {
    if ( typeof oTable != 'undefined' ) {
    oTable.fnGetColumnData();
    }
    }
    } );
    oTable.fnGetColumnData();
    } );

    $(document).ready(function() {
    oTable = $('#example').dataTable( {
    "fnDrawCallback": function () {
    setTimeout( function () {
    oTable.fnGetColumnData();
    }, 0 );
    }
    } );
    } );
    [/code]

    The first one will get the column data after the object has been fully inited and then on each draw callback, while the second one (which it rather a hack...) uses a settimeout to interrupt the execution thread, allowing the object to fully initialise...

    Allan
  • zygimantaszygimantas Posts: 33Questions: 0Answers: 0
    That's awesome! Thank you Allan. The second one worked perfectly!
  • greitsmagreitsma Posts: 3Questions: 0Answers: 0
    I have used the fnDrawCallback to begin the collection and display of stats about the items selected. There might be another place to get a callback, perhaps after filtering is complete, since sorting does not change the selected set as does filtering this could have been done after filtering; but fnDrawCallback works fine. I do a jQuery search for various elements in "this.aaData" and display some counts.

    I also used fnDrawCallback to reattach some event handling that was lost; but I guess this is no longer needed.
  • talkitivewizardtalkitivewizard Posts: 30Questions: 0Answers: 0
    edited March 2010
    I'm having a problem similar to this one and it involves one of your examples:
    Check out http://www.datatables.net/examples/server_side/row_details.html

    I've been putzing around with this for a bit and I realized that in the following function:
    [code]
    var oTable;
    function fnOpenClose ( oSettings )
    {
    $('td img', oTable.fnGetNodes() ).each( function () {
    $(this).click( function () {
    ...
    } );
    } );
    }
    ...
    $(document).ready(function() {
    oTable = $('#example').dataTable( {
    "bProcessing": true,
    "bServerSide": true,
    "sAjaxSource": "../examples_support/server_processing_details_col.php",
    ...
    "fnDrawCallback": fnOpenClose
    } );
    } );
    [/code]

    Line 4 is exactly what you are describing here isn't it?

    If it is.. do you have an alternative?

    I'll continue to work on tis problem till I find a work around... if I do, I'll give you a heads up.
  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin
    Hi talkitivewizard,

    The reason my example works is that fnDrawCallback isn't called until the first draw - which is done asynchronously due to the Ajax call. So oTable should be initialised by the time fnDrawCallback is called. So what is the specific problem you are having?

    Allan
  • talkitivewizardtalkitivewizard Posts: 30Questions: 0Answers: 0
    I'm getting the error:
    trigger_report_table is undefined
    code/allanc/pages/email2/js/trigger_activity_report.js
    Line 15
    my exact code is this:
    [code]
    var trigger_report_table;
    function fnFormatDetails ( nTr )
    {
    var aData = trigger_report_table.fnGetData( nTr );
    var sOut = '';
    sOut += 'Rendering engine:'+aData[2]+' '+aData[5]+'';
    sOut += 'Link to source:Could provide a link here';
    sOut += 'Extra info:And any further details here (images etc)';
    sOut += '';

    return sOut;
    }
    function fnOpenClose ( oSettings )
    {
    jQuery('td img', trigger_report_table.fnGetNodes() ).each( function () {
    $(this).click( function () {
    var nTr = this.parentNode.parentNode;
    if ( this.src.match('small_minus') )
    {
    /* This row is already open - close it */
    this.src = "http://mailcenterdev.newmediagateway.com/code/global/images/icons/small_plus.gif";
    /* fnClose doesn't do anything for server-side processing - do it ourselves :-) */
    var nRemove = jQuery(nTr).next()[0];
    nRemove.parentNode.removeChild( nRemove );
    }
    else
    {
    /* Open this row */
    this.src = "http://mailcenterdev.newmediagateway.com/code/global/images/icons/small_minus.gif";
    trigger_report_table.fnOpen( nTr, fnFormatDetails(nTr), 'details' );
    }
    } );
    } );
    }
    jQuery(document).ready(function() {
    url = 'index.php?p=email2.trigger_activity_report&page_action=getData';
    trigger_report_table = jQuery("#trigger_report").dataTable({
    bJQueryUI: true,
    bProcessing: true,
    sDom: '<"template-box"lfri><><"ui-widget ui-widget-content ui-helper-clearfix ui-corner-all template-box" t><"template-box"<"template-box toolbar" >p>',
    sAjaxSource: url,
    bStateSave: true,
    bPaginate: false,
    sPaginationType: "full_numbers",
    bAutoWidth: true,
    "fnDrawCallback": fnOpenClose
    });
    });
    [/code]

    This is why I assume that this is a simular issue.
  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin
    Ah okay - you aren't using server-side processing - which is the trick here... DataTables will actually do a draw before the Ajax data has loaded (I'm wondering if that is actually a bug... I'll bare it in mind and investigate shortly). This first draw occurs during the table's initialisation - which is why you are getting this error. I think the way to solve it is in your fnOpenClose function, simply put a check for "typeof trigger_report_table == 'undefined'" and return if that is the case. Then when the draw occurs for the real data - it will run this function fully.

    Allan
  • talkitivewizardtalkitivewizard Posts: 30Questions: 0Answers: 0
    you know... It's always the simple ideas like that, that amaze me.

    Kinda like when you find that semicolon that was causing IE to iexplode.

    Thanks from one Allan to another Allan,

    Allan Chappell
  • EvanCarrollEvanCarroll Posts: 7Questions: 1Answers: 0
    edited July 2010
    re: Allan, # talkitivewizard

    This just bite me, I'd consider it a bug. I tried to port the `row detail` example to my static datatables -- exactly like talkitivewizard. @Allan, examples shouldn't be written in such a way that you can describe their function as "a trick" that relies on a side effect of using the server side functionality.

    This example does not work for me, even with the modifications.
  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin
    Sorry - I think I didn't make myself quite clear - the meaning I was using for the word "trick" was not that it's a cunning or deceitful thing to do, but rather a bit of information that was missing. The example doesn't rely on trick code, but rather knowing how the software works. On thinking about the draw issue, I don't believe it is a bug, and is in fact potentially fairly useful. For the first draw DataTables will remove whatever might be in the tbody, and stick in it's empty data row, that will then be replaced when the Ajax data is inserted. That could be useful as a loading message - equally it might not. So not a bug, but something to be aware of :-)

    Allan
  • morthahnamorthahna Posts: 18Questions: 0Answers: 0
    edited August 2010
    I have the same problem. Workaround doesn't work for me... Maybe I missed something? A quick solution would be wonderful... donation is on the way :-)

    This is how i tried it:
    [code]
    "fnDrawCallback": function () {
    if ( typeof oTable != 'undefined' ) {
    fnOpenClose;
    };
    },
    [/code]
  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin
    Hi morthahna,

    Are you also using server-side processing?

    If so, I suspect the only thing you need to do is actually call fnOpenClose (rather than the line you've got at the moment which won't actually do anything!):

    [code]
    "fnDrawCallback": function () {
    if ( typeof oTable != 'undefined' ) {
    fnOpenClose();
    };
    },
    [/code]
    Regards,
    Allan
  • morthahnamorthahna Posts: 18Questions: 0Answers: 0
    the missing () does the trick... thanks for the quick help!
  • morthahnamorthahna Posts: 18Questions: 0Answers: 0
    edited August 2010
    Now it works, but only on the first page. When i go to page 2, opening the details do not work..
    Instead, when i try it on the second page and go back to the first page, i see open details at the first page... ^^
  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin
    Are you using fnGetNodes() as in the example code above?

    [code]
    jQuery('td img', oTable.fnGetNodes() ).each( function () {
    [/code]
    for example. With that, it should work regardless of what page it is on. However, it should probably work anyway with server-side processing... Could you post your initialisation code please? Or even better, a link!

    Allan
  • morthahnamorthahna Posts: 18Questions: 0Answers: 0
    [code]

    var oTable;

    /* Formating function for row details */
    function fnFormatDetails ( nTr )
    {
    var aData = oTable.fnGetData( nTr );
    var sOut = '';
    sOut += '';
    return sOut;
    }

    /* Event handler function */
    function fnOpenClose ( oSettings )
    {
    $('td img', oTable.fnGetNodes() ).each( function () {
    $(this).click( function () {
    var nTr = this.parentNode.parentNode;
    if ( this.src.match('details_close') )
    {
    /* This row is already open - close it */
    this.src = "img/datatable/details_open.png";
    /* fnClose doesn't do anything for server-side processing - do it ourselves :-) */
    var nRemove = $(nTr).next()[0];
    nRemove.parentNode.removeChild( nRemove );
    }
    else
    {
    /* Open this row */
    this.src = "img/datatable/details_close.png";
    oTable.fnOpen( nTr, fnFormatDetails(nTr), 'details' );
    }
    } );
    } );
    }



    $(document).ready(function() {
    oTable = $('#datatable').dataTable( {
    "sAjaxSource": 'rpc.php?action=doclist',
    "sPaginationType": 'full_numbers',
    "bAutoWidth": false,
    "aaSorting": [[1, 'desc']],
    "fnDrawCallback": function () {
    if ( typeof oTable != 'undefined' ) {
    fnOpenClose();
    };
    },
    "fnRowCallback": function( nRow, aData, iDisplayIndex ) {
    var id = aData[5];
    $(nRow).attr("id",id);
    return nRow;
    },
    "aoColumns" : [
    { "sClass": "center", "bSortable": false },
    { "fnRender": function ( oObj ) {
    //var id = "test";
    //$(oObj).attr("id",id);
    return "" + oObj.aData[1] + "";
    } },
    null,
    null,
    null,
    null,
    { "fnRender": function ( oObj ) {
    return "";
    } }
    ]


    } );
    } );

    [/code]
  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin
    You aren't using server-side processing - which makes this slightly different. Change this:

    [code]
    "fnDrawCallback": function () {
    if ( typeof oTable != 'undefined' ) {
    fnOpenClose();
    };
    },
    [/code]
    To this:

    [code]
    "fnInitComplete": function () {
    fnOpenClose();
    },
    [/code]
    And let me know how you get on.

    Allan
  • EvanCarrollEvanCarroll Posts: 7Questions: 1Answers: 0
    edited August 2010
    @allan, I rewrote this example entirely. My implementation caches the additional contents with .show()/.hide(), and it works on the row, rather than on the fnDrawCallback.

    Just create a column classed as .moreInfo. + , etc -- I think I'd nest in an tag for old IE's but this works great for me.

    Also, feel free to move the $tr element above the call to .dataTable if you want to cache the jQuery object and then just .clone() it or something of that sort.
    [code]
    $(document).ready( function () {
    oTable = $("table").dataTable({
    bJQueryUI: true
    , bPaginate: true
    , bAutoWidth: false
    , fnRowCallback: function( nRow, aData, iDisplayIndex ) {

    var $tr = $('').append(
    $('', {
    'colspan': $(nRow).children('td').length
    , 'text': 'HALLO WIN'
    } )
    )

    // we must unbind because nRow will be the same on next page
    var ins = false
    $(nRow).children('.moreInfo').unbind().toggle(
    function () {
    if ( ins ) {
    $tr.show()
    }
    else {
    ins = true
    $(nRow).after( $tr )
    }
    }
    , function () {
    $tr.hide()
    }
    )

    return $(nRow).get(0)
    }
    })
    } )
    [/code]

    Feel free to use it, please give feedback.
  • morthahnamorthahna Posts: 18Questions: 0Answers: 0
    Hey Allan, the change you proposed does change the behavior a little bit... Details can be opened on other pages, but when i open up items on other pages and go back to the first, it has open details on some rows...

    Any suggestions?

    @EvanCarroll: i will try to adapt to your example also...
  • EvanCarrollEvanCarroll Posts: 7Questions: 1Answers: 0
    @morthana, I'd be curious to know if this worked for you.
    @allan, I'd be curious to know what you think of this implementation vs. the one currently in the example?
  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin
    It looks perfectly fine to me :-). It doesn't provide quite the same functionality, since fnClose won't work with it - but that doesn't matter - as long as it does what you need it to.

    Allan
  • morthahnamorthahna Posts: 18Questions: 0Answers: 0
    @allan: but it doesn't fix the problem in your example...

    [code]
    "fnInitComplete": function () {
    fnOpenClose();
    },
    [/code]

    ... doesn't help either.

    May i provide you with a test account to our prototype via email?
  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin
    Yes indeed: http://datatables.net/contact .

    Allan
This discussion has been closed.