fnAddData, jeditable and keytable

fnAddData, jeditable and keytable

NympheasiNympheasi Posts: 24Questions: 0Answers: 0
edited January 2011 in General
Hi,

I wonder if it's possible in dataTables to dynamically add a new row with fnAddData et makes keytable and jeditable working on this new row.

When I add a new item in textbox (which is in div.scan) and press return key to following code call server-side data and add a new row. But I don't know how to add this row in keytable and jeditable at the same time. I want to be able to modify the new row with jeditable but using functionality of keytable.

[code]
$("div.scan input").live("keypress", function(e) {
if (e.keyCode == 13) {
var ein = $(this).val();
$.getJSON("templates/warehouse_test_decoders/ajax/ein_details.php", {searchedEin : ein}, function(data) {
oTable.fnAddData( ["",data.ein, data.ein_desc, data.tech_diag, data.tech_comments, data.date, data.tech_name, data.headquarter,"","",""] );
});
$(this).val("");
}
})
[/code]

Any Idea?

Replies

  • NympheasiNympheasi Posts: 24Questions: 0Answers: 0
    I don't mind if I need to donate to get an answer. But before I just want to know if this something that could be done? I've found similar threads on the forum but there's no answer to these...
  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin
    Donations / support requests are always welcome (top of the page :-) ).

    What I would suggest is doing the fnAddData and then taking the return from that and adding the jEditable initialiser to make the new cells editable (http://datatables.net/api#fnAddData - it returns an array index which you can look up and use the node as a jQuery selector for jEditable).

    Allan
  • NympheasiNympheasi Posts: 24Questions: 0Answers: 0
    Donation sent! With an working example, it would be easier to figure how I can do that. :)
  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin
    Hi Nympheasi,

    Here is how it can be done with the return from fnAddData:

    [code]
    $(document).ready(function() {
    /* Init DataTables */
    var oTable = $('#example').dataTable();

    /* Apply the jEditable handlers to the table */
    $('td', oTable.fnGetNodes()).editable( '../examples_support/editable_ajax.php', {...} );

    var aiData = oTable.fnAddData( [1, 2, 3, 4, 5] );
    var oSettings = oTable.fnSettings();
    $('td', oSettings.aoData[ aiData[0] ].nTr).editable( '../examples_support/editable_ajax.php', {...} );
    } );
    [/code]
    The array aiData contains pointers to where the new row is in the internal DataTables data store (aoData) - so you can get the TR node (.nTr) from that and then perform any operation you want on it - in this case adding jEditable event listeners.

    Would be nice if jEditable provided a $.live() option, and there are a few posts around the web saying how to do that, if you wanted to go that route, but this one should do nicely :-)

    Regards,
    Allan
  • NympheasiNympheasi Posts: 24Questions: 0Answers: 0
    edited January 2011
    Hi Allan,

    This code is working to apply jeditable on new row on all td's. How can I apply it only , by example, td #5 and td #6.
    And I want this to work with keytable and trigger jeditable on return keypress is this possible? I can't find the way to apply keytable to row added with fnAddData.

    Here's the code I use for now:

    [code]
    $("div.scan input").live("keypress", function(e) {
    if (e.keyCode == 13) {
    var ein = $(this).val();
    $.getJSON("templates/warehouse_test_decoders/ajax/ein_details.php", {searchedEin : ein}, function(data) {
    var aiData = oTable.fnAddData( ["",data.ein, data.ein_desc, data.tech_diag, data.tech_comments, data.date, data.tech_name, data.headquarter,"","",""] );
    var oSettings = oTable.fnSettings();
    $("td", oSettings.aoData[ aiData[0] ].nTr).editable( "templates/warehouse_test_decoders/ajax/ein_details.php");
    });
    $(this).val("");
    }
    })

    [/code]

    Note: I did not put jeditable option in code for this example.

    Any Idea?
  • NympheasiNympheasi Posts: 24Questions: 0Answers: 0
    Ok, I found it to apply jeditable on specific td. But I still can't find a way to make it working with keytable...

    [code]
    $("td:eq(5)", oSettings.aoData[ aiData[0] ].nTr).editable("templates/warehouse_test_decoders/ajax/ein_details.php");
    [/code]
  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin
    Indeed - the jQuery selector is the key to deciding which cell should be used by jEditable.

    What code are you using for KeyTable? I just recently updated the example code for jEditable with KeyTable to take into account jEditable 1.7.1 - the change can be found here: https://github.com/DataTables/KeyTable/blob/master/editing.html . If you are using a KeyTable action like that, you shouldn't need to add the jEditable handler with the fnAddData.

    Regards,
    Allan
  • NympheasiNympheasi Posts: 24Questions: 0Answers: 0
    Here's something working. But one point is bugging me. Reinitialise var keys for the whole table on every added row. Not sure if doing it the best way...

    [code]
    $("div.scan input").live("keypress", function(e) {
    if (e.keyCode == 13) {
    var ein = $(this).val();
    $.getJSON("templates/warehouse_test_decoders/ajax/ein_details.php", {searchedEin : ein}, function(data) {
    var aiData = oTable.fnAddData( ["",data.ein, data.ein_desc, data.tech_diag, data.tech_comments, data.date, data.tech_name, data.headquarter,"","",""] );
    var oSettings = oTable.fnSettings();
    $("td:eq(0)", oSettings.aoData[ aiData[0] ].nTr).html(\'\');
    $("td:eq(9)", oSettings.aoData[ aiData[0] ].nTr).addClass("edit_comments");
    var keys = new KeyTable( {
    "table": document.getElementById("example")
    } );
    /* Apply a return key event to each cell in the table */
    keys.event.action( null, null, function (nCell) {
    /* Block KeyTable from performing any events while jEditable is in edit mode */
    keys.block = true;
    if($(nCell).hasClass("edit_comments") == true){
    /* Initialise the Editable instance for this table */
    $(nCell).editable( "templates/tech/ajax/edit_comments.php", {
    callback: function(value, settings) {
    keys.block = false;
    $(this).editable("destroy");
    },
    submit : "OK",
    cancel : "Cancel",
    indicator : "Saving...",
    placeholder: " ",
    "onblur": "cancel",
    "onreset": function(){
    /* Unblock KeyTable, but only after this "esc" key event has finished.
    * Otherwise it will "esc" KeyTable as well
    */
    $(nCell).editable("destroy");
    setTimeout( function () {keys.block = false;}, 0);
    }
    } );
    /* Dispatch click event to go into edit mode - Saf 4 needs a timeout... */
    setTimeout( function () { $(nCell).click(); }, 0 );
    }else{
    keys.block = false;
    }
    } );
    });
    $(this).val("");
    }
    });
    [/code]
  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin
    Good to hear that you've got it working - although the code does look a little bit on the inefficient side to me :-) I've rewritten it below, although I've not got your table to test it on, so there might be a few small changes that need to be made:

    [code]

    function fnEditable ( nCell )
    {
    /* Block KeyTable from performing any events while jEditable is in edit mode */
    keys.block = true;
    /* Initialise the Editable instance for this table */
    $(nCell).editable( "templates/tech/ajax/edit_comments.php", {
    callback: function(value, settings) {
    keys.block = false;
    $(this).editable("destroy");
    },
    submit : "OK",
    cancel : "Cancel",
    indicator : "Saving...",
    placeholder: " ",
    "onblur": "cancel",
    "onreset": function(){
    /* Unblock KeyTable, but only after this "esc" key event has finished.
    * Otherwise it will "esc" KeyTable as well
    */
    $(nCell).editable("destroy");
    setTimeout( function () {keys.block = false;}, 0);
    }
    } );
    /* Dispatch click event to go into edit mode - Saf 4 needs a timeout... */
    setTimeout( function () { $(nCell).click(); }, 0 );
    }


    $(document).ready( function () {
    var oTable = $('#example').dataTable( {
    "aoColumnDefs": [
    {
    "fnRender": function (o) {
    return '';
    },
    "aTargets": [ 0 ]
    },
    {
    "sClass": "edit_comments",
    "aTargets": [ 9 ]
    }
    ]
    /* and whatever else initialisation is needed */
    } );

    var keys = new KeyTable( {
    "table": document.getElementById("example"),
    "datatable": oTable
    } );

    keys.event.action( 5, null, fnEditable ); /* Providing editing on the 6th column */
    keys.event.action( 6, null, fnEditable ); /* Providing editing on the 7th column */

    $("div.scan input").live("keypress", function(e) {
    if (e.keyCode == 13) {
    var ein = $(this).val();
    $.getJSON("templates/warehouse_test_decoders/ajax/ein_details.php", {searchedEin : ein}, function(data) {
    oTable.fnAddData( ["",data.ein, data.ein_desc, data.tech_diag, data.tech_comments, data.date, data.tech_name, data.headquarter,"","",""] );
    });
    $(this).val("");
    }
    });
    } );
    [/code]
    So you are right, the initialisation of 'keys' every time is inefficient, and will quickly consume RAM. So what can be done is to take advantage of the KeyTables events, which are effectively 'live' - you don't need to add a new instance of KeyTable for every new row - using 'null' as the event location for the column and/or row will allow it to always work on that column / row.

    So in the example above I've got the editable function being called on the 6th and 7th columns, again using the KeyTable event options (not sure if these are the right columns for you, but easy to change!).

    I've also made use of the aoColumnDefs option in DataTables to apply some of the formatting that you were using.

    Regards,
    Allan
  • NympheasiNympheasi Posts: 24Questions: 0Answers: 0
    I tried this code but it doesn't work. It seems that keyTable is not applied to added row via fnAddData. Is it supposed to?
  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin
    It certainly should be :-). I've tested code which is very similar to the above - it initialises DataTables, then KeyTable and then adds a row - the row is editable. Can you possibly give me a link to your page which isn't working?

    Regards,
    Allan
  • NympheasiNympheasi Posts: 24Questions: 0Answers: 0
    I can't give you a URL because the site is hosted on an Intranet, but here's the complete javascript I use on this page:

    [code]
    < script type = "text/javascript" charset = "utf-8" >

    function fnEditable(nCell) {
    /* Block KeyTable from performing any events while jEditable is in edit mode */
    keys.block = true;
    /* Initialise the Editable instance for this table */
    $(nCell).editable("templates/tech/ajax/edit_comments.php", {
    callback: function (value, settings) {
    keys.block = false;
    $(this).editable("destroy");
    },
    submit: "OK",
    cancel: "Cancel",
    indicator: "Saving...",
    placeholder: " ",
    "onblur": "cancel",
    "onreset": function () {
    /* Unblock KeyTable, but only after this "esc" key event has finished.
    * Otherwise it will "esc" KeyTable as well
    */
    $(nCell).editable("destroy");
    setTimeout(function () {
    keys.block = false;
    }, 0);
    }
    });
    /* Dispatch click event to go into edit mode - Saf 4 needs a timeout... */
    setTimeout(function () {
    $(nCell).click();
    }, 0);
    }

    function fnFormatDetails(oTable, nTr) {
    var aData = oTable.fnGetData(nTr);
    var date = aData[3];
    var workorder = aData[1];
    var eqID = aData[2];
    $.ajax({
    type: "POST",
    url: "templates/tech/ajax/workorder_details.php",
    cache: false,
    data: ({
    search_term: date + "+" + workorder + "+" + eqID + "+supervisors"
    }),
    success: function (msg) {
    oTable.fnOpen(nTr, msg, "details");
    }
    });
    return "";
    }

    $(document).ready(function () {
    TableToolsInit.sSwfPath = "javascript/DataTables/extras/TableTools/media/swf/ZeroClipboard.swf";
    var oTable = $("#example").dataTable({
    "bFilter": false,
    "bJQueryUI": true,
    "sDom": '<"H"l<"scan">r>t<"F"i"T"p>',
    "aoColumnDefs": [{
    "fnRender": function (o) {
    return '';
    },
    "aTargets": [0]
    },
    {
    "sClass": "edit_comments",
    "aTargets": [9]
    },
    {
    "bSortable": false,
    "aTargets": [0]
    }]
    });
    $("div.scan").html("Rechercher : ");
    $("div.scan").css("float", "right");
    var keys = new KeyTable({
    "table": document.getElementById("example"),
    "datatable": oTable
    });
    keys.event.action(9, null, fnEditable); /* Providing editing on the 9th column */
    $("div.scan input").live("keypress", function (e) {
    if (e.keyCode == 13) {
    var ein = $(this).val();
    $.getJSON("templates/warehouse_test_decoders/ajax/ein_details.php", {
    searchedEin: ein
    }, function (data) {
    oTable.fnAddData(["", data.ein, data.ein_desc, data.tech_diag, data.tech_comments, data.date, data.tech_name, data.headquarter, "", "", ""]);
    });
    $(this).val("");
    }
    });

    /* 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 = "javascript/DataTables/examples/examples_support/details_open.png";
    oTable.fnClose(nTr);
    } else {
    /* Open this row */
    this.src = "javascript/DataTables/examples/examples_support/details_close.png";
    oTable.fnOpen(nTr, fnFormatDetails(oTable, nTr), "details");
    }
    });
    });
    < /script>
    [/code]
  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin
    Great thanks for that! Could you possibly try the 'nightly' version of KeyTable (from this page http://datatables.net/download/ )? I've just committed a change which uses live events for the click to focus option in KeyTable, which is wasn't before, and I suspect that was the issue you were seeing).

    Thanks,
    Allan
  • NympheasiNympheasi Posts: 24Questions: 0Answers: 0
    Ok, that's better. Now I can see KeyTable applied on every new row but when i hit the return key on the 9th cell I got the following error: keys is not defined.
  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin
    Sorry my mistake - keys is a variable defined in the document.ready function, and is local to there, but since I moved the editable part out into it's own function, it still needs a reference to keys and it isn't available any more. So a number of options:

    1. Make keys global
    2. Pass keys through an anon function
    3. Move the function back into the document.ready one - since you are only using it once.

    Here is how option 2 can be done:

    [code]
    < script type = "text/javascript" charset = "utf-8" >
    function fnEditable(keys, nCell) {
    /* Block KeyTable from performing any events while jEditable is in edit mode */
    keys.block = true;
    /* Initialise the Editable instance for this table */
    $(nCell).editable("templates/tech/ajax/edit_comments.php", {
    callback: function (value, settings) {
    keys.block = false;
    $(this).editable("destroy");
    },
    submit: "OK",
    cancel: "Cancel",
    indicator: "Saving...",
    placeholder: " ",
    "onblur": "cancel",
    "onreset": function () {
    /* Unblock KeyTable, but only after this "esc" key event has finished.
    * Otherwise it will "esc" KeyTable as well
    */
    $(nCell).editable("destroy");
    setTimeout(function () {
    keys.block = false;
    }, 0);
    }
    });
    /* Dispatch click event to go into edit mode - Saf 4 needs a timeout... */
    setTimeout(function () {
    $(nCell).click();
    }, 0);
    }

    function fnFormatDetails(oTable, nTr) {
    var aData = oTable.fnGetData(nTr);
    var date = aData[3];
    var workorder = aData[1];
    var eqID = aData[2];
    $.ajax({
    type: "POST",
    url: "templates/tech/ajax/workorder_details.php",
    cache: false,
    data: ({
    search_term: date + "+" + workorder + "+" + eqID + "+supervisors"
    }),
    success: function (msg) {
    oTable.fnOpen(nTr, msg, "details");
    }
    });
    return "";
    }

    $(document).ready(function () {
    TableToolsInit.sSwfPath = "javascript/DataTables/extras/TableTools/media/swf/ZeroClipboard.swf";
    var oTable = $("#example").dataTable({
    "bFilter": false,
    "bJQueryUI": true,
    "sDom": '<"H"l<"scan">r>t<"F"i"T"p>',
    "aoColumnDefs": [{
    "fnRender": function (o) {
    return '';
    },
    "aTargets": [0]
    },
    {
    "sClass": "edit_comments",
    "aTargets": [9]
    },
    {
    "bSortable": false,
    "aTargets": [0]
    }]
    });
    $("div.scan").html("Rechercher : ");
    $("div.scan").css("float", "right");
    var keys = new KeyTable({
    "table": document.getElementById("example"),
    "datatable": oTable
    });
    keys.event.action(9, null, function (node) {
    fnEditable( keys, node );
    } ); /* Providing editing on the 9th column */
    $("div.scan input").live("keypress", function (e) {
    if (e.keyCode == 13) {
    var ein = $(this).val();
    $.getJSON("templates/warehouse_test_decoders/ajax/ein_details.php", {
    searchedEin: ein
    }, function (data) {
    oTable.fnAddData(["", data.ein, data.ein_desc, data.tech_diag, data.tech_comments, data.date, data.tech_name, data.headquarter, "", "", ""]);
    });
    $(this).val("");
    }
    });

    /* 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 = "javascript/DataTables/examples/examples_support/details_open.png";
    oTable.fnClose(nTr);
    } else {
    /* Open this row */
    this.src = "javascript/DataTables/examples/examples_support/details_close.png";
    oTable.fnOpen(nTr, fnFormatDetails(oTable, nTr), "details");
    }
    });
    });
    < /script>
    [/code]
    Regards,
    Allan
  • NympheasiNympheasi Posts: 24Questions: 0Answers: 0
    Many many thanks to you Allan! It works exactly as expected!

    Thanks for your support. It is really appreciated! :-)
  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin
    My pleasure - great to hear it does the trick :-)

    Regards,
    Allan
  • NympheasiNympheasi Posts: 24Questions: 0Answers: 0
    Just a quick point: Is there a way to make keytable navigation applied only to what's on screen? I mean not applied to hidden rows. I use hidden row which I toggle with .hide and .show to hide or show additional infos. But Keytable is applied on each of them.

    Thanks!
  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin
    No that isn't something that KeyTable currently supports. The only way it supports hidden rows is through the DataTables method of actually removing the node from the DOM. KeyTable should work fine with fnOpen / fnClose which are the two built in methods for DataTables to show a 'details' row appended to any parent row - are you using that, or a custom method? If a custom method, then a change would be needed to KeyTables to support that.

    Regards,
    Allan
  • NympheasiNympheasi Posts: 24Questions: 0Answers: 0
    In fact I use fnAddTr to add new rows to datatables instead of fnAddData. Depending on the requested item I append variable number of rows to DataTables. The first one is shown and the other are hide but available via toggle button. I decided to go this way beacuse I was unable to add variable number of rows with fnOpen with the same format as regular rows (I mean regular TR with same columns width, colors, zebra, etc...).

    Here's the code I use for that:

    [code]
    $("div.scan input").live("keypress", function(e) {
    if (e.keyCode == 13) {
    var ein = $(this).val();
    $.get("templates/warehouse_test_decoders/ajax/ein_details.php", {searchedEin : ein}, function(data) {
    var n = $(data).filter("tr").length;
    for ( var i=0 ; i
  • allanallan Posts: 63,498Questions: 1Answers: 10,470 Site admin
    Sounds fair - although fnAddTr actually adds the row to DataTables as an independent row - not a child of a given target. This can mean that sorting / filtering can throw it out of position, unless you do something like using aaSortingFixed.

    So with this method, KeyTable thinks that the added row is a part of the table (since it is!) and thus will treat it as the same. It is probably possible to have KeyTable skip rows / cells with a certain class, but it's not something I've looked at before.

    Regards,
    Allan
This discussion has been closed.