ColReorder and rowCallback don't play nicely with each other

ColReorder and rowCallback don't play nicely with each other

doug@feif.orgdoug@feif.org Posts: 5Questions: 1Answers: 0

It looks like these two features have a bad interaction. In my real application I have used the rowCallback to decorate a fixed first column in the table with some icons replacing the underlying text. As soon as I start to drag a column to reorder I see the undecorated text reappear. I also am having to add an extra draw() call after the table initialization to get the decoration to render correctly.

I've thrown together a quick jsfiddle ( http://jsfiddle.net/d6p2b/z8meddv6/ ) that uses colReorder and a rowCallback for some trivial cell decoration (bold if over 30). This fiddle nicely replicates the reorder issue but not my problem with needing the extra draw().

If you run the fiddle and...

  1. Sort the age column descending
  2. Drag the age column one place to the left
  3. Page through the data

.. you will see the text decoration has gone whack.

Am I doing something wrong here or is there, perhaps, a missing invocation of the callback?

This question has an accepted answers - jump to answer

Answers

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

    Hi,

    Thanks for the fiddle. The issue here is the data[3] - specifically, the age information is no longer in that position if the columns have been reordered. If you move it one to the left, then the age information would now be in data[2].

    There are a number of ways to address this - all requiring a bit of extra code I'm afraid.

    1. The most obvious is to update the index: http://jsfiddle.net/z8meddv6/1/ .
    2. This index could also be updated using the column reordering mapping
    3. Another option is to use columns.createdCell rather than rowCallback. That is called only once so the index will be "correct" when called. The disadvantage is that there is no updatedCell callback if you happen to change the data?
    4. Using objects rather than arrays would remove the need to track the data index (since you could just use data.age which doesn't change). Combining that with a class name for which column to update (columns.className) would remove the need to handle indexes entirely.

    If you don't need to update the data, then 3 is the best option, if you do and you can change to using objects, 4 is best option. Otherwise 1 or 2.

    In general with column reordering its best to remove any dependency on indexes - it quickly gets confusing otherwise!

    Allan

  • doug@feif.orgdoug@feif.org Posts: 5Questions: 1Answers: 0

    Thank for the quick reply, Allan. Everything you are making makes perfect sense.

    I'm not sure my fiddle really captured my situation. In my real-world case the decoration is on the fixed first column. Let me see if I can adjust the fiddle to better represent my problem.

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

    Regardless of FixedColumns, the ColReorder index issue would remain. FixedColumns shouldn't actually have any effect on that aspect.

    Allan

  • doug@feif.orgdoug@feif.org Posts: 5Questions: 1Answers: 0

    I still think something else has to be going on. The decorated column is fixed in position 0 and the callback is defined as:

    "rowCallback" : function(row, data, index) {
      // replace the contents of the first column (rowid) with an edit link
      $('td:first', row).html("<a href='#' class='glyphicon glyphicon-edit'></a>").click(openPersonEdit);
      console.log(index);
    },
    

    Clearly there is nothing here that depends on any column indexes.

    As soon as I start to drag any column to a new position I see the original html (rowid integers from the database) in column 0 and my nice glyphicons go away. (I've replaced the contents of the .html() with just plain text. No dice until I send another .draw() )

    In case this offers any clues... There is also the odd behavior on initialization I mentioned in my original post. When I initialize the table I see the callback invoked for 10 rows. The strange thing is the .click() bit worked but the .html() didn't. I still see my rowid integers. If I add a .draw() to the initialization I see the callback called another 10 times and now the .html() and .click() have worked.

    As soon as I start the drag I see no calls to the callback but I lose the modified html.

    I can send you a link to the live page in a private email if you'd like. I realize that my fiddle isn't good enough to communicate the situation and this reply is a whole bunch of hand-waiving. I'm rather embarrassed to be sending it as I'm asking you to help but not letting you see the the problem for yourself.

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

    Ah! Thanks for hte link - absolutely clear now.

    The issue is that you are updating the DOM ($('td:first', row).html( ...) - but DataTables doesn't know you've done that. So, when the column is re-ordered and DataTables invalidates its cache (reads form the document and updates the display for the new column order) the original data is redisplayed (since rowCallback doesn't get triggered here).

    I would suggest, rather than using rowCallback (which is going to be bad with that event anyway - go to page 2, then back to page 1 and click the icon - the click event will trigger twice because it has been added twice!), use the columns.defaultContent option for this column:

    {
      data: null,
      defaultContent: "<a href='#' class='glyphicon glyphicon-edit'></a>"
    }
    

    And for the event do:

    $('#pTable tbody').on( 'click', 'td:first-child', openPersonEdit );
    

    Regards,
    Allan

    edit - sorry, hit sumbit early!

  • doug@feif.orgdoug@feif.org Posts: 5Questions: 1Answers: 0

    Yes. This all makes sense. I was too focussed on where I thought the problem must be to think about the DOM / DT interaction. Of course, you are completely correct that I am stacking on click events as well.

    A couple academic questions as I'm sure you solution will get the job done for me...

    Is there are reason not to trigger the rowCallback on the reorder?

    Any thoughts about why I needed that extra .draw() when I first initialize the table to get the .html() to have an impact?

    Thanks!

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

    Is there are reason not to trigger the rowCallback on the reorder?

    ColReorder doesn't do a draw. The draw is avoided to try and improve performance (particularly if server-side processing is used) since it isn't really needed in this case - the data is the same. The row callback is only called when the table is drawn.

    Any thoughts about why I needed that extra .draw() when I first initialize the table to get the .html() to have an impact?

    Not a clue! I can't see why that would be needed I'm afraid. It certainly shouldn't be.

    Allan

  • doug@feif.orgdoug@feif.org Posts: 5Questions: 1Answers: 0

    Thanks for the quick and friendly answers. I'm willing to be my extra draw() won't be needed once I follow your suggested solution.

This discussion has been closed.