Keep child rows open after ajax reload

Keep child rows open after ajax reload

dpanscikdpanscik Posts: 202Questions: 47Answers: 0

When I do an ajax reload on the parent table, I would like to keep all childs open.

There are quite a few posts on this topic both in the forums here and on stack overflow. None of which I was able to get working.

One post seemed to indicate the API at some point started to support this.
https://datatables.net/forums/discussion/61778/keep-child-rows-open-on-draw

I have tied to turn on responsive to enable this feature but when I do so, the layout changes in a weird way.

With "responsive": false, the carrot is on the right side of the table and the child table opens just fine

With "responsive": true, the carrot shifts to the left side of the table and the child table does not open.

Here is a screenshot with the child table open with `"responsive": true,

Answers

  • kthorngrenkthorngren Posts: 21,330Questions: 26Answers: 4,951

    The first problem is you can't have both Child Detail Rows (which I believe you have assigned the last column) and Responsive child rows. See the compatibility matrix. You will need to use a modal display for Responsive like this example.

    The thread you linked to is specific to Responsive child rows and using the row id.

    Here is an example using ajax.reload() with the callback function to loop through the collected open rows to reopen Child Detail Rows. This blindly does the work without making sure its reopening the correct row. Test to make sure it works for your solution. You may need to use rows().ids() to make sure to reopen the appropriate rows.
    https://live.datatables.net/niwimexu/1/edit

    Kevin

  • dpanscikdpanscik Posts: 202Questions: 47Answers: 0

    Thank you for this sample. Ill pick up this project again in the morning.

  • dpanscikdpanscik Posts: 202Questions: 47Answers: 0

    Kevin,

    I couldn't get your example to work either. I am missing something obvious that should be staring me in the face.

    When the main table refreshes, all the childs close and they do not reopen.

    I put my code on datatables live, its an editor solution so I don't think it will run on datatables.live but at least you can see what I am doing and perhaps spot what I am doing wrong.

    https://live.datatables.net/yalasixo/1/edit

  • kthorngrenkthorngren Posts: 21,330Questions: 26Answers: 4,951

    You can use the JS Bin Add library menu option to add Editor and the other extensions. For example:
    https://live.datatables.net/yalasixo/2/edit

    However the test case is using an ajax url that is not accessible.

    I guess this is the code you are having difficulties with?

            noteEditor.on('submitSuccess', function () {
                console.log("submit success triggered");
                noteTable.rows().every(function () {
                    if (this.child.isShown()) {
                        updateChild(this);
                    }
                });
    
                // Get shown rows
                noteChildRows = mainTable.rows('tr.shown');
                
                //mainTable.ajax.reload();
                // Reload the Datatable
                mainTable.ajax.reload(function (json) {
    
                    // Loop open child rows to reopen
                    noteChildRows.every(function (rowIdx, tableLoop, rowLoop) {
                        d = this.data();
    
                        this.createNoteChild(rowIdx, d());
                        this.nodes().to$().addClass('shown');
                    });
                });
            });
    

    I would first do some debugging to see if noteChildRows = mainTable.rows('tr.shown'); is finding the shown child rows.

    Next I would debug this loop:

                    noteChildRows.every(function (rowIdx, tableLoop, rowLoop) {
                        d = this.data();
    
                        this.createNoteChild(rowIdx, d());
                        this.nodes().to$().addClass('shown');
                    });
    

    Make sure d = this.data(); is the correct row. Not sure this is correct:

    this.createNoteChild(rowIdx, d());
    

    I think you will want to remove the this. part as you are just calling a function.

    Kevin

  • dpanscikdpanscik Posts: 202Questions: 47Answers: 0

    I didn't know there was a shortcut to add in js. into datatables.live That is certainly helpful.

    data tables live is responding with an console message "error on line 0". Not sure what that means, but I see the JSON strings are not loading.

    noteChildRows = mainTable.rows('tr.shown');

    This gets the row number. such as 0 1 2 3 but the row numbers are really row_860 row_861 row_862

    I wonder if this is not the issue. Or at least the first issue. All the examples out there that show how to do this dont harvest row numbers in the format row_861 but instead harvest 0 1 2 3

  • dpanscikdpanscik Posts: 202Questions: 47Answers: 0

    The message I get in browser console is row data is not a fuction

    This error doesn't make a whole lot of sense. It was obviously a function when the child was originally created, why its not a function now is a very odd.

  • dpanscikdpanscik Posts: 202Questions: 47Answers: 0

    I figured out my issue in part.

    All the examples use a subroutine named "format" where the html that creates the child table is expressly typed. I let datatables make the table html so I thought I didnt need this fuction. Turns out I do... I dont want to expressly type the html that datatables does automatically.

    How can I turn this into the automatic datatables version?

    function format(d) {
        // `d` is the original data object for the row
        //console.log(d);
        return '<table class="display childGrid" width="90%"/>' +
            '<tr>' +
            '<td>Full name:</td>' +
            '<td>' + d.name + '</td>' +
            '</tr>' +
            '<tr>' +
            '<td>Extension number:</td>' +
            '<td>' + d.extn + '</td>' +
            '</tr>' +
            '<tr>' +
            '<td>Extra info:</td>' +
            '<td>And any further details here (images etc)...</td>' +
            '</tr>' +
            '</table>';
    }
    
  • kthorngrenkthorngren Posts: 21,330Questions: 26Answers: 4,951
    edited August 18

    I noticed another issue:

            noteChildRows.every(function (rowIdx, tableLoop, rowLoop) {
                d = this.data();
     
                this.createNoteChild(rowIdx, d());
                this.nodes().to$().addClass('shown');
            });
        });
    

    You have d() in the createNoteChild(rowIdx, d()) function call. Remove the parentheses as this is not a function but a Javascript variable.

    I let datatables make the table html

    Datatables doesn't make the table HTML. You need to create this. You don't have to use the format() function. You are doing the same as the format function with the table variable in this code:

        function createNoteChild(row, data) {
    
            // This is the table we'll convert into a DataTable
            //var table = $('<table class="display childGrid" width="100%"/>');
            var table = $('<table class="display childGrid" width="90%"/>');
            // Display it the child row
            row.child(table).show();
    

    Kevin

  • dpanscikdpanscik Posts: 202Questions: 47Answers: 0

    Hi Kevin,

    Here is my updated code

                mainTable.ajax.reload(function (json) {
    
                    // Loop open child rows to reopen
                    childRows.every(function (rowIdx, tableLoop, rowLoop) {
                        d = this.data();
    
                        this.createNoteChild(rowIdx, d);
                        this.nodes().to$().addClass('shown');
                        
                    });
                });
    

    I am now getting two errors.

    Error #1 - createNoteChild is not a function

    Error #2 - nodes is not a function

  • kthorngrenkthorngren Posts: 21,330Questions: 26Answers: 4,951

    Error #1 - createNoteChild is not a function

    As I mentioned before:

    this.createNoteChild(rowIdx, d());
    I think you will want to remove the this. part as you are just calling a function.

    Error #2 - nodes is not a function

    I think this is due to a change in Datatables 2.0. 1.0 allowed for row().nodes() (plural nodes) to work even though there is no API for this. The loop is one row at a time so this is equivalent to api row(). Use this instead:

    this.node().addClass('shown');
    

    Kevin

  • dpanscikdpanscik Posts: 202Questions: 47Answers: 0

    Hi Kevin,

    Here is the latest update of code

                // Reload the Datatable
                mainTable.ajax.reload(function (json) {
    
                    // Loop open child rows to reopen
                    childRows.every(function (rowIdx, tableLoop, rowLoop) {
                        d = this.data();
    
                        //this.createNoteChild(rowIdx, d);
                        this.node().addClass('shown');
                        
                    });
                });
    
    

    And here is the most recent error;

  • dpanscikdpanscik Posts: 202Questions: 47Answers: 0

    Ive retooled and gone back to this example;
    https://datatables.net/blog/2019/parent-child-editing-in-child-rows

    Scroll down to the bottom and look at "Updating the parent table"

    This is exactly what I am trying to do... but it also does not work.
    The child closes on reload.

    Here is my code;

            noteEditor.on('submitSuccess', function (e, json, data, action) {
                row.ajax.reload(function () {
                    $(row.cell(row.id(true), 0).node()).click();
                });
                console.log("submit success triggered");
    
            });
    
  • kthorngrenkthorngren Posts: 21,330Questions: 26Answers: 4,951
    edited August 18

    this.node().addClass('shown');

    Sorry I believe this should look like this:

    $( this.node() ).addClass('shown');
    

    $(row.cell(row.id(true), 0).node()).click();

    The cell() API takes the following parameters in order, row, cell. You are referring to the first cell in the row with 0. I think you said the child row click element is in the last column. Change the index to match.

    The example I gave reopens all open child rows on ajax.reload(). The solution provided in the blog only reopens the row that was edited. Maybe this is all you want.

    Kevin

  • dpanscikdpanscik Posts: 202Questions: 47Answers: 0

    Very nice. We are now updating the class. Now to get the actual opening of the child table working.

    Here is my latest code.

                // Reload the Datatable
                mainTable.ajax.reload(function (json) {
    
                    // Loop open child rows to reopen
                    childRows.every(function (rowIdx, tableLoop, rowLoop) {
                        d = this.data();
    
                        this.child( $(format( d ) )).show();
                        $(this.node()).addClass('shown');
    
                    });
                });
    

    for this.child( $(format( d ) )).show(); i dont need to trigger the function named format

    so I think that all I need to reopen a child is $(this.node()).show(); so I change my code as follows.

                // Reload the Datatable
                mainTable.ajax.reload(function (json) {
    
                    // Loop open child rows to reopen
                    childRows.every(function (rowIdx, tableLoop, rowLoop) {
                        d = this.data();
    
                        $(this.node()).show();
                        $(this.node()).addClass('shown');
    
                    });
                });
    

    but it doesn't work the child stays closed after the reload.

  • kthorngrenkthorngren Posts: 21,330Questions: 26Answers: 4,951

    $(this.node()).show();

    This is using jQuery show() which won't work.

    Maybe row().child.show() will work. Something like this:

    this.child.show();
    

    Kevin

  • dpanscikdpanscik Posts: 202Questions: 47Answers: 0
    edited August 18

    no luck. the child stays closed.

    here is my latest code

            noteEditor.on('submitSuccess', function (e, json, data, action) {
                console.log("submit success triggered");
                noteTable.rows().every(function () {
                    if (this.child.isShown()) {
                        updateChild(this);
                    }
                });
                // Get shown rows
                var childRows = mainTable.rows('tr.shown');
    
                mainTable.ajax.reload(function (json) {
    
                    // Loop open child rows to reopen
                    childRows.every(function (rowIdx, tableLoop, rowLoop) {
                        d = this.data();
    
                        this.child.show();
                        $(this.node()).addClass('shown');
    
                    });
                });
    
            });
    
    
  • kthorngrenkthorngren Posts: 21,330Questions: 26Answers: 4,951

    Have you tried using createNoteChild(rowIdx, d); in place of this.child.show();?

    Possibly you can adapt the click() method used in the blog:

    noteEditor.on('submitSuccess', function (e, json, data, action) {
        console.log("submit success triggered");
        noteTable.rows().every(function () {
            if (this.child.isShown()) {
                updateChild(this);
            }
        });
        // Get shown rows
        var childRows = mainTable.rows('tr.shown');
     
        mainTable.ajax.reload(function (json) {
     
            // Loop open child rows to reopen
            childRows.every(function (rowIdx, tableLoop, rowLoop) {
                d = this.data();
     
                $( mainTable.cell( this, 0 ).node() ).click();
                $(this.node()).addClass('shown');
     
            });
        });
     
    });
    

    Replace the 0 index with the column index you have the chid row details button in.

    Kevin

  • dpanscikdpanscik Posts: 202Questions: 47Answers: 0

    I feel like we are getting in the weeds here and need to backup and establish a baseline.

    Going back to this example as a baseline;
    https://live.datatables.net/niwimexu/1/edit

    Lets use a button click to retrigger the action of reloading the parent table and reopening the child table.

    The result, an incorrect version of the child table loads.

    Here is the code;
    https://live.datatables.net/yalasixo/3/edit

    disclaimer: one of the java script includes is throwing an error so the above example does not work but all the code is there and it does indeed work in Visual Studio. Everything is the same except in Visual Studio the JSON string originates from a database, in the live.datatable example the JSON string is a fixed string.

    Step #1 - Open a child table

    Step #2 - Click on reload table

    Step #3 - Notice the wrong child table loads

    Obviously the wrong child table loads, its pulling from the format function. How can I reload the original child table that we see in Step #1 ?

    -David

  • kthorngrenkthorngren Posts: 21,330Questions: 26Answers: 4,951
    edited August 20

    You are getting this error:

    SyntaxError: Unexpected token ':'

    Click on the Errors area at the bottom of the Javascript tab. You have this:

        var editor = new DataTable.Editor({
            ajax: response: parentJSON,
    

    I commented out the ajax option.

    Now this error:

    ReferenceError: parentJSON is not defined

    This won't work:

            "serverSide": true,
            ajax: {
                response: parentJSON,
    

    if you want to add Javascript data then use data not ajax. See this example. Also remove the serverSide option since you aren't using the ajax. Or use one of the SSP JS Bin templates.

    When you click the row to open the child you use this code:

                // Open this row
                //format(row.data());
                createNoteChild(row, row.data());
                tr.addClass('shown');
    

    Instead of using the format function:

                childRows.every(function (rowIdx, tableLoop, rowLoop) {
                    d = this.data();
    
                    this.child($(format(d))).show();
                    $(this.node()).addClass('shown');
    
                });
    

    Why not call the createNoteChild function:

                childRows.every(function (rowIdx, tableLoop, rowLoop) {
                    d = this.data();
    
                    createNoteChild(( this, this.data() );
                    $(this.node()).addClass('shown');
    
                });
    

    Kevin

  • dpanscikdpanscik Posts: 202Questions: 47Answers: 0

    regarding the live.datatables

    Im trying to get a working example that we can both use, there are errors, but I cannot see the difference between erros and warnings and I believe I am missing at least one warning or error that is not displayed on the list.
    Here is a link to a video on dropbox to show you what I am seeing;
    https://dropbox.com/scl/fi/ev36mmkziinm41zbml26w/ErrorsAndWarnings.mp4?rlkey=yxo4d0kzjcr9dff314zsxja49&dl=0

    regarding why I dont use the createNoteChild funciton, I get this error message when I call it. Which is very perplexing as I dont get that error when the function is called the first time around.

    table.GR_AlternativeBilling_Reload_001.js:41 Uncaught TypeError: row.child is not a function
        at createNoteChild (table.GR_AlternativeBilling_Reload_001.js:41:7)
        at U.<anonymous> (table.GR_AlternativeBilling_Reload_001.js:537:5)
        at U.<anonymous> (datatables.min.js:36:85829)
        at U.iterator (datatables.min.js:36:56129)
        at U.<anonymous> (datatables.min.js:36:85710)
        at U.every (datatables.min.js:36:57250)
        at table.GR_AlternativeBilling_Reload_001.js:533:14
        at HTMLTableElement.<anonymous> (datatables.min.js:36:58720)
        at HTMLTableElement.i (datatables.min.js:14:44962)
        at HTMLTableElement.dispatch (datatables.min.js:14:48528)
    

  • kthorngrenkthorngren Posts: 21,330Questions: 26Answers: 4,951

    How are you calling createNoteChild?

    My suggestion is this:

    childRows.every(function (rowIdx, tableLoop, rowLoop) {
        d = this.data();
     
        createNoteChild(( this, this.data() );
        $(this.node()).addClass('shown');
     
    });
    

    this in the rows().every() loop is the row API for the row. Should match how you are using the row parameter in the function. this.data() is the row().data() for the row. At least thats how I beleive it to be. You can put a debugger break point on createNoteChild(( this, this.data() ); to see what the two values actually are.

    Kevin

  • dpanscikdpanscik Posts: 202Questions: 47Answers: 0

    I am exactly calling based on your suggestion that throws the error row.Child is not a function.

    Here is the call I am using;

                // Loop open child rows to reopen
                childRows.every(function (rowIdx, tableLoop, rowLoop) {
                    d = this.data();
    
                    
                    createNoteChild((this, this.data()));   //does not work
                    $(this.node()).addClass('shown');
    
                });
    
  • kthorngrenkthorngren Posts: 21,330Questions: 26Answers: 4,951
    edited August 22

    I had a bit of time to build a test case utilizing much of your code. Did not include Editor but created two buttons' One to call createNoteChild() and the other to use the click method found in the blog.
    https://live.datatables.net/yaroyala/41/edit

    Both options seem to work.

    createNoteChild((this, this.data())); //does not work

    I building the test case I noticed an extra ( in my suggested code snippet. Looks like you added an extra ) which is causing (this, this.data()) to be passed into the createNoteChild()'s row parameter. Nothing is passed to the data parameter. It should look like this:

    createNoteChild(this, this.data()); 
    

    Kevin

  • dpanscikdpanscik Posts: 202Questions: 47Answers: 0

    Well its always something simple isn't it? Golly.

    The next couple of weeks I am working on other projects. Ill circle back around to this in approximately two weeks.

    Thanks for the assistance Kevin. I appreciate it!

  • dpanscikdpanscik Posts: 202Questions: 47Answers: 0

    So with Kevin's most recent suggestion, the reload of the child table still didn't work. Although Kevin;s most recent suggestion did help to correct a typing error that was causing issues. There was still something aims.

    I then thought to myself... what if the attempt to reopen the child table is just triggering to soon? So I put a 2 second delay on the child reopen loop, and it worked!!!!

    Here is my code showing the 2 second delay.

        function reloadChild() {
    
            console.log("here in reloadChild");
    
            // Get shown rows
            childRows = mainTable.rows('tr.shown')
    
            // Reload the Datatable
            mainTable.ajax.reload(function (json) {
    
                setTimeout(function () {
                    // Loop open child rows to reopen
                    childRows.every(function (rowIdx, tableLoop, rowLoop) {
                        console.log("this ");
                        console.log(this);
                        console.log("this.data ");
                        console.log(this.data());
    
                        d = this.data();
    
                        this.child($(format(d))).show();
                        createNoteChild(this, this.data());   //does not work
                        $(this.node()).addClass('shown');
    
                    });
                }, 2000);
    
            });
        }
    

    This is the hook I am using to trigger the child table reload, on editor submit. It was just triggering way to soon.

            noteEditor.on('submitSuccess', function (e, json, data, action) {
                console.log("submit success triggered");
                noteTable.rows().every(function () {
                    if (this.child.isShown()) {
                        updateChild(this);
                    }
                });
    
    
                reloadChild(childRows);
    
    
            });
    

    I then move the trigger to the child editor's AJAX "success" response, removed my 2 second delay, and PRESTO! It worked!

  • kthorngrenkthorngren Posts: 21,330Questions: 26Answers: 4,951

    That seems strange. You might need to wait on the DOM to be updated but 2 seconds seems a lot. Have you tried a smaller number like 100 or 200?

    Kevin

  • dpanscikdpanscik Posts: 202Questions: 47Answers: 0

    I didn't test for a shorter time, I just immediately moved the trigger to JSON:Success with no delay and it worked

  • kthorngrenkthorngren Posts: 21,330Questions: 26Answers: 4,951
    edited September 1

    Are you saying you added the success function to the ajax option? Doing so is not supported. This is from the ajax docs:

    success - Must not be overridden as it is used internally in DataTables. To manipulate / transform the data returned by the server use ajax.dataSrc (above), or use ajax as a function (below).

    Maybe use the xhr event instead.

    Kevin

Sign In or Register to comment.