Code broken after updating to latest libs

Code broken after updating to latest libs

pain19pain19 Posts: 57Questions: 5Answers: 0
edited September 4 in Free community support

DESCRIPTION: In an older version of the libs, I had code which would perform Archive, Trash Bin, and Restore operations. In DT terms, I'd be on a table of (10) rows, perform one of the operations, and essentially delete the selected rows from the table and redraw it. This has stopped working since I updated so obviously something is wrong in my call that the new libs do not like. Hopefully the below code will give you enough to point me in the right direction.

Before Code (this worked perfectly)
Adding Index to column (2) of the table:

        // Populate the # column with the index method.
        tableConfig.on( 'order.dt search.dt', function () {
            tableConfig.column(2, {search:'applied', order:'applied'}).nodes().each( function (cell, i) {
                cell.innerHTML = i+1;
                tableConfig.cell(cell).invalidate('dom');
            });
        }).draw(resetPage);

Archiving Operation (removing selected rows from the current table)

            archiveTableToUpdate.rows('.selected').every( function(rowIdx, tableLoop, rowLoop) {
                
                // Remove the remaining rows as the API successfully deleted them from the DB.
                archiveTableToUpdate.row('.selected').remove().draw(false);
            });

_Current Code _ (taken from the example on the datatables.net page)
Still adding Index to column (2)

        tableConfig.on('order.dt search.dt', function() {
            let rowNumber = 1;
            let rowIdx = tableConfig.row(this).index();
            
            tableConfig
            .cells(rowIdx, 2, {search: 'applied', order: 'applied'})
            .every(function(cell) {
                this.data(rowNumber++);
            });
        }).draw(resetPage);

ERROR
TypeError: null is not an object (evaluating 't.aoData[n]._aData') - datatables.min.js:11200:178

NOTE: If I comment out both versions of the index code, everything works perfectly. So I'm not processing something properly when the row is removed and the Index needs to be redrawn but I cannot figure out where.

In the old row.index code I am performing an .invalidate operation which I think I need. However, I've tried placing this in various places with no success. Also, I am no longer using "dom" to draw things as I switched over to the "layout" functionality which I believe I'm supposed to use. But this may also be the issue as the dom may not be available--maybe I need a little bit of the dom, but it's not there now and if I comment out row.index() code everything works just fine.

Any help is appreciated. I hope the provided code is enough as my app is large now and not sure pasting all of it is beneficial. :)

Edited by Kevin: Syntax highlighting. Details on how to highlight code using markdown can be found in this guide

Replies

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

    Before Code (this worked perfectly)

    This code still seems to work in this test case running Datatables 2.1.5:
    https://live.datatables.net/japonabi/1/edit

    Note I changed .draw(resetPage) to .draw() as I'm not sure what resetPage is.

    _Current Code _ (taken from the example on the datatables.net page)

    There are a couple issues here. For the cells() row-selector you should pass null, which means all rows, as the first parameter. Looks like you are trying to pass in a row index. See the example.

    I did the same with resetPage. Test case with this updated code:
    https://live.datatables.net/haqiqese/1/edit

    Archiving Operation (removing selected rows from the current table)

    This loop doesn't seem right:

    archiveTableToUpdate.rows('.selected').every( function(rowIdx, tableLoop, rowLoop) {
         
        // Remove the remaining rows as the API successfully deleted them from the DB.
        archiveTableToUpdate.row('.selected').remove().draw(false);
    });
    

    It seems it might work but its lopping through one or more rows using .rows('.selected').every(). In the loop its using .row('.selected').remove() which will return only the first row found. You could just use this:

    archiveTableToUpdate.rows('.selected').remove().draw();
    

    A best practice option, when using the Select extension, is to use the selector-modifier of { selected: true }, like this:

    archiveTableToUpdate.rows( (selected: true ).remove().draw();
    

    If you still need help please provide a link to a test case replicating the issues so we can help debug.
    https://datatables.net/manual/tech-notes/10#How-to-provide-a-test-case

    Kevin

  • pain19pain19 Posts: 57Questions: 5Answers: 0

    @kthorngren Roger that and as always, THANK YOU for the quick response! I'll make the modifications and review your examples for me. Since I am updating to the latest libs, I am taking the time to also clean up any of my bad code and align things with the best practices. Stay tuned :sunglasses:

  • pain19pain19 Posts: 57Questions: 5Answers: 0

    /@kthorngren ok, I'm going to try to mock this up in a test case for you. But here's what I have discovered.

    On initial load of my tables, both index code snippets work just fine. But it's on a redraw where it is breaking. For example,

    Row 1
    Row 2
    Row 3

    I remove one of the rows (doesn't matter which one) and it removes the row successfully, but breaks on my .draw() on my archiveTableToUpdate.rows( (selected: true ).remove().draw().

    If I remove the .draw() the code continues successfully--of course it doesn't update the table in my view. Or if I comment out the code that provides the indexes and leave the .draw() on, it successfully completes and redraws the table.

    In summary, if I use the index code it seems like I need to invalidate the indexes somehow because again, if I don't use indexes everything works just fine. Anyway, stay tuned as I try to get this mocked up and hopefully I can break it so you can see what I mean. :sunglasses:

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

    I remove one of the rows (doesn't matter which one) and it removes the row successfully, but breaks on my .draw()

    This clue helped. It does seem there is a difference in the way Datatables 1.x and 2.x works when using row().remove().

    Here are simple test cases that shows the behavior difference using column().nodes() in the order, search and draw in the 1.x and 2.x. Open the browser's console to see the results of clicking the Delete Ashton button.

    1.13.11 works:
    https://live.datatables.net/buhayopa/1/edit

    2.1.5 results in this error:

    Uncaught TypeError: Cannot read properties of null (reading 'anCells')

    https://live.datatables.net/japonabi/2/edit

    2.1.5 has the same error with column().data().

    @allan will need to take a look.

    Kevin

  • pain19pain19 Posts: 57Questions: 5Answers: 0

    Awesome, glad I am not crazy :wink: I will review what you've provided and wait to hear back from @allan. Worse case for me at the moment is I just remove that column and continue updating the my code to work with the latest libs in other areas. :smile:

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

    Worse case for me at the moment is I just remove that column

    The issue has nothing to do with the index column. My example simply use row().remove() then outputs the following in each event:

    console.log(tableConfig.column(2, {search:'applied', order:'applied'}).nodes().count());
    

    There is no code manipulating the index column. It seems something isn't properly updated in the Datatables cache causing the error when using column.nodes() in the order event handler. The 2.1.5 test case now works after commenting out the console.log statement in the order event:
    https://live.datatables.net/japonabi/3/edit

    Kevin

  • pain19pain19 Posts: 57Questions: 5Answers: 0

    @kthorngren my apologies, I didn't mean to imply that the index was still the issue. Only that I can disable it and everything works for me. So if I need to move on, I can as having the indexes is just a nice-to-have in my app.

    I looked at your example, and col 2 doesn't have any data in it. I have duplicated this in my code and it works. For me, as long as col 2 doesn't display a value it works. It's only when an actual value is present/displayed that it breaks.

    But you've given me quite a bit to noodle on and as a "very" old school developer, I shy away from being spoon fed. So, I'm going to see what I can come up with based on your feedback.

    One idea I had is to populate col 2 at the time I generate the table data. Currently, I generate the table data away from the index. I'll report back after I've done some more testing on my end. I think this is a simple issue, but I just can't put my finger on it just yet.

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

    For me, as long as col 2 doesn't display a value it works. It's only when an actual value is present/displayed that it breaks.

    The problem is not that you have indexes or display a value in column 2. The problem occurs when using column().nodes() in the order event. I believe this is a bug in Datatables and if it is then Allan will fix it as it could affect other solutions.

    One idea I had is to populate col 2 at the time I generate the table data.

    You could move the code into either initComplete or init so it runs only once. The index value won't change on sorting or searching.

    Another option is to place the code in the draw event. However it will execute anytime there is a draw event including paging. This will allow the index to update and start with 1 at the top of the firs page. However it will run more often even when its not needed to change the index.

    The option you choose is based on your requirements.

    Kevin

  • pain19pain19 Posts: 57Questions: 5Answers: 0

    My apologies, just saw the update you made on my initial comment for code highlighting. Will use that going forward.

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

    Please note I am not arguing or disagreeing with you at all. At this point, I feel it is "when" I am choosing to include the indexes.

    I know you aren't arguing but there seems to be a bug within Datatables that when the order event is fired the removed row hasn't been updated. I updated my test case with this for each event:

    console.log(tableConfig.cells(null, 2, {search: 'applied', order: 'applied'}).count())
    

    https://live.datatables.net/japonabi/5/edit

    On table load the output shows this:

    order
    runner-3.17.11.min.js:1 5
    runner-3.17.11.min.js:1 search
    runner-3.17.11.min.js:1 5
    runner-3.17.11.min.js:1 draw
    runner-3.17.11.min.js:1 5
    

    After deleting a row the output shows this;

    runner-3.17.11.min.js:1 order
    runner-3.17.11.min.js:1 5
    runner-3.17.11.min.js:1 search
    runner-3.17.11.min.js:1 4
    runner-3.17.11.min.js:1 draw
    runner-3.17.11.min.js:1 4
    

    The table should have 4 rows and after deleting but the order event still sees 5 rows. However the search and draw events see the remaining 4. As I said before there appears to be a bug in Datatables, with the order of events, where the order event is fired before the row is removed.

    This is something @allan will need to look at.

    Kevin

  • pain19pain19 Posts: 57Questions: 5Answers: 0

    Understood. But I also tried the newer code example found here that does not use columns().node().

    So, to get the indexing I have tried the following:
    NEW CODE

            tableConfig.on('order.dt search.dt', function() {
                let rowNumber = 1;
                
                tableConfig
                .cells(null, 2, {search: 'applied', order: 'applied'})
                .every(function(cell) {
                    this.data(rowNumber++);
                });
            }).draw(resetPage);
    

    ERROR
    Unhandled Promise Rejection: TypeError: null is not an object (evaluating 't.aoData[n]._aData') --- datatables.min.js:34:66928

    OLD CODE

            tableConfig.on( 'order.dt search.dt', function () {
                tableConfig.column(2, {search:'applied', order:'applied'}).nodes().each( function (cell, i) {
                    cell.innerHTML = i+1;
                    tableConfig.cell(cell).invalidate('dom');
                });
            }).draw(resetPage);
    

    ERROR
    Unhandled Promise Rejection: TypeError: null is not an object (evaluating 'e[t[o]][n]') --- datatables.min.js:34:873

    Ignoring the "resetPage" variable as it doesn't matter for this troubleshooting.

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

    Just of r fun remove the order.dt from tableConfig.on( 'order.dt search.dt', function () {. What happens?

    Kevin

  • pain19pain19 Posts: 57Questions: 5Answers: 0
    edited September 5

    BINGO!!! Removing order.dt in both code snippets resulted in success. @kthorngren I cannot thank you enough for helping through this.

    Let me know if this should be the final implementation or if something needs to be fixed in the lib and I should continue to use order.dt. For now, I can move forward with my updating. :sunglasses:

    UPDATE: I just saw your previous reply regarding the order.dt being called too soon. So I will keep an eye out for the fix and update my code accordingly. But at least I can move forward now.

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

    TL;DR for @allan

    See this test case:
    https://live.datatables.net/japonabi/5/edit

    The table starts with 5 rows. Using row().remove() followed by draw() causes the order event to fire. The count() of cells() inside order still shows 5 but should be 4. It seems row().remove() isn't fully removing the row which looks like its cleaned up by the ordering process. Using column().nodes() or column().data() in order causes exception errors.

    Kevin

  • pain19pain19 Posts: 57Questions: 5Answers: 0

    @kthorngren not sure if this is related but in the old DT version I was using, the index column did not update when the other columns were sorted. It stayed 1, 2, 3, ... when say, the Project Name column was sorted. However, now it is updating when the columns are sorting so 1, 2, 3, ... becomes 10, 9, 8, ...

    Other than the order.dt you had me take out, I have not changed anything else. Any ideas? Thx.

    PS: I did search for this but even the Column Index example the indexes are not changing when the table is sorted.

  • pain19pain19 Posts: 57Questions: 5Answers: 0

    UPDATE: Never mind my question, it is because the order.dt has been removed. So, I'll just plan on waiting on Allan and the fix to rollout.

  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin

    This one gets right into the depths of DataTables :). Thanks for flagging this up and also to Kevin for your test cases and analysis.

    tl;dr: fix committed here, and it will be in the release later today.

    One of the big changes in DataTables 2 was to switch the internal data store to be a sparse array. So when you delete row index 2, another row doesn't suddenly become row index 2, rather index 2 is now null and can't be replaced (server-side processing is an exception). This was done as API instances will keep their index reference, even after a row is deleted, so you might suddenly start operating on a different row (it was unusual, but it wasn't a good way to handle things).

    This means that if we try to access information about row index 2, it is going to fail with an error message about being unable to access an array index, like you are seeing.

    This is where it gets grizzly - when a row is deleted, I update the "master" index, but not the "draw" index arrays (due to this fix). The "draw" index array is updated on the next draw - so there is a period of time after a row().remove() call, that if you query the drawn table, it will still refer to the deleted row. If you remove the {search:'applied', order:'applied'} it works fine since it uses the "master" index, but with that it uses the "draw" index.

    The order event happens before the "draw" index is updated - thus the error!

    I can't update the draw index on delete, per the previous fix, but what I have done is added a little extra check to make sure that the data being referenced is not null.

    Phew - that woke me up on a Friday morning!
    Allan

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

    Very nice, thanks for the detailed explanation.

    Kevin

  • pain19pain19 Posts: 57Questions: 5Answers: 0

    @allan thank you for the detailed explanation and the fix! I know I say this every time I post, but you guys truly ROCK!!! And I am so happy I found DT years ago. My apps have been well-received because of the functionality DT provides. Thanks a million!!!!!

  • pain19pain19 Posts: 57Questions: 5Answers: 0

    @allan just saw that 2.1.6 was available, so I updated to point to it (I was on 2.1.4). Anyway, I am getting the same error message when I add "order.dt" back into my code.

    Works fine without "order.dt", but then the index column becomes a part of the sort, which I do not want.

    Just let me know if I am missing something. As I told @kthorngren, I can simply remove my index column permanently and move on. I'm not sure how much my customers care about having an index column anyway--I like it myself. B)

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

    There still seems to be some issues to work out. This test case uses your first example with column().nodes(). The exception error doesn't occur but the indexes aren't updated when the table is sorted by another column:
    https://live.datatables.net/japonabi/11/edit

    Order by the Position column but the output of the Name column is in the previous order:

    Ashton Cox
    Cedric Kelly
    Garrett Winters
    Jenna Elliott
    Tiger Nixon
    

    Delete Ashton and the indexes are updated properly.

    Using the cells().every() solution in this test case results in this error:

    Cannot read properties of null (reading '_aData')

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

    Note the output of the number of cells remains 5 after deleting Ashton.

    @allan will need to look further.

    Kevin

  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin

    Perhaps .every() should be checking that the row it is given hasn't been deleted, however, that wouldn't resolve it for multi-page tables since {search: 'applied', order: 'applied'} reads from the display index and thus would be wrong.

    If you need to update data to create something like an index (which is what I presume is happening here), do it in draw rather than the order / search events.

    I'll need to have a deeper think on the architecture of this - perhaps those events need to be delayed.

    Allan

  • pain19pain19 Posts: 57Questions: 5Answers: 0

    @allan sorry for the delay in response. I will see if I can move the index routines into the .draw() function and if not, I can simply remove that column as it is the only thing that breaks after since I've updated to all of the latest libs. I also think I can simply create my own index when I build the other columns in the table. That way, it doesn't refresh the indexes until the user refreshes the entire table.

    For example, I have rows 1, 2, 3. I delete row 2. The .draw() would update so the table shows 1 and 3. However, if the user clicks my table refresh button, it would reset the index so it become 1 and 2. Not sure if that is viable to the end users, but it doesn't really bothering me at all. Plus, it might be ok to know what rows I deleted (in this case row 2) before refreshing the table.

    Anyway, I don't think this is a show stopper and as I've stated I can always remove the index column from the table and move on. Thanks for looking into this for me and I'll keep an eye out just in case you resolve this issue in a more permanent way. B)

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

    Create a function to build the indexes. Use initComplete to call the function to generate the indexes on table initialization. Call the function to refresh the indexes if desired.

    Kevin

  • pain19pain19 Posts: 57Questions: 5Answers: 0

    Roger that @kthorngren I forgot you recommended that in an earlier post. Thanks for the help and guidance, I should be able to resolve this issue today and move on. I updated to all the new libs b/c I want to start using the Editor libs so I wanted to be sure all lib versions were properly aligned. ;)

  • pain19pain19 Posts: 57Questions: 5Answers: 0

    @allan and @kthorngren my sincere apologies as I totally missed this line in Allan's original post regarding the fix he applied:

    " If you remove the {search:'applied', order:'applied'} it works fine since it uses the "master" index, but with that it uses the "draw" index."

    As I was rereading this thread today, I saw it, removed it and everything works just fine now with the rest of my code "as is". We can close this bad-boy out now.

    Thank you for all the guidance and patience on this one!

  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin

    No worries - thanks for letting us know. Good to hear you have a solution and thanks for brining this up. It's been an interesting topic and one I need to think further about.

    Allan

Sign In or Register to comment.