invalidate('dom') throws exception for cells/rows which are not visible

invalidate('dom') throws exception for cells/rows which are not visible

TalaganTalagan Posts: 10Questions: 2Answers: 0

Description of problem:

In recent versions of datatables, when calling invalidate('dom') on cells or rows, if these rows / cells are not visible (they don't have a dom representation), an exception will be raised.

The exception is located in the _fnGetRowElements, around this block of code :

// Existing row object passed in
tds = row.anCells;
    
for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
    cellProcess( tds[j] );
}

For these specific rows, tds is null.

Test case

See the attached file for a test case : clicking the button will try to invalidate all rows, but only some of them are visible due to pagination. So, the invalidate function will perform an iteration until it reaches the first non visible row, triggering the exception.

Expected behaviour

This should probably be ignored silently. In my use case, I use ignore('dom') to perform real-time updates on the datatable, not knowing if the cell/row is currently shown or not. Maybe it is easy to fix by just handling the cases when things are null (by skipping the processing), in _fnGetRowElements and _fnInvalidate ?

Cheers,

Ben

This question has an accepted answers - jump to answer

Answers

  • allanallan Posts: 61,972Questions: 1Answers: 10,160 Site admin

    Interesting one. To some degree, this error is not a bad thing, because invalidate('dom') is telling DataTables to read the data from the DOM elements. But if they haven't been created, then it can't - so I think the error might actually be correct.

    Perhaps it should not invalidate rows where the cells haven't been created yet, but then I'm not sure that would be obvious to the developer using DataTables, and the intention wouldn't be carried out.

    Perhaps what you need to do is disable the defer render option (deferRender) so that the DOM nodes are present when you want them.

    Another would be to use rows().nodes() to then create a selector based on row nodes, so you wouldn't be attempting to update from the document when the cells aren't present.

    How are you doing the updates at the moment - are you writing to individual cells and then blanket calling invalidate on all rows? You coul do it on the specific cells that are being updated which would be more efficent.

    Allan

  • TalaganTalagan Posts: 10Questions: 2Answers: 0

    The use case is similar to the ones I posted a long time ago in this thread.

    There's a column with a custom widget, defined with data: null, that has a custom renderer. If I quote my own example, it looks like this :

                {
                  data: null,
                  type: 'num', // Important for sorting.
                  render: function(data, type, row, meta ) {
                    if(type=='display') {
                      return "<span class='colthumb'></span>";
                    }
                    else if(type=='sort')
                    {
                      // This converts a string color to a number
                      return colLuminosity(data.color);
                    }
                    else
                    {
                      return data.color;
                    }
                  }
               }
    

    And row callbacks :

              createdRow: function (row, entry, dataIndex) {
                var thumb = $('td',row).eq(1).find(".colthumb");
                thumb.click(function() {
                  alert("My current color is " + entry.color + ", luminosity " + colLuminosity(entry.color));
                });
              },
              rowCallback: function(row, entry, dataIndex) {
                var thumb = $('td',row).eq(1).find(".colthumb");
                thumb.css('background-color',entry.color);
              }
    

    To reload data for these specific columns, I used to use invalidate('dom'), but after looking closer, I'm not sure if it's relevant. Maybe calling invalidate('data') is a sufficient and a better approach to get the cells updated ?

  • allanallan Posts: 61,972Questions: 1Answers: 10,160 Site admin
    Answer ✓

    invalidate('dom') tells DataTables to read the data for the row from the document. But if you update the table using an API method such as row().data(), then you don't need to invalidate the route - DataTables will do it for you.

    If you are getting new JSON data to display, that's how I would suggest you do it.

    Allan

  • TalaganTalagan Posts: 10Questions: 2Answers: 0

    Thanks, Allan, this really helps a lot ! After a bit of struggle, I reimplemented my column differently using a function for data, instead of using a render callback :

                  type: 'num',
                  data: function (row, type, val, meta) {
                    if(type == 'set') {
                      row.color = val.color;
                      return row.color;
                    }
                    else if(type === 'display') {
                      return "<span class='colthumb' style='background-color:" + row.color + "'></span>";
                    }
                    else if(type === 'sort')
                    {
                      return colLuminosity(row.color);
                    }
                    else
                    {
                      return row.color;
                    }
                  }
    

    Now I can invalidate a single cell, and trigger a single redraw using :

    ...
    sharedData[rowToPatch].color = newcol;
    datatable.cell(rowToPatch, 1).data(sharedData[rowToPatch]);
    

    It seems to work very well.

    However, I noticed that triggering that kind of updates in the early life of the table may result in a wrong class affectation in the DOM for some cells regarding sorting (they should all be tagged as 'dt-type-numeric sorting_1' but some of them might received only 'sorting_1' when playing with pagination. Is there something I'm doing wrong ?

    Here's a fiddle illustrating this :

    https://jsfiddle.net/efrgxdt1/1/

    The first rows are randomly changed with a timer, simply clicking on page 2 will show that some cells don't get the 'dt-type-numeric' class.

Sign In or Register to comment.