Table data duplicating

Table data duplicating

krytenkryten Posts: 4Questions: 1Answers: 0

Description of problem:
I am using a DataTable that gets a JSON from a PHP script. The ajax query is intercepted by jQuery's ajax prefilter that checks if a 401 error has occurred so it can issue a auth refresh and re-run the original query. My DataTables load fine but the data is duplicated when the ajax request has been re-issued. I attached a draw event to the table and it fires an extra time when the duplication occurs. There is no duplication when I do an ajax reload or I refresh the page and the initial query doesn't hit a 401. How can I suppress the additional draw? It's not feasible to do the ajax auth refresh in the DT setup. Also I am forced to do the AJAX in the DT setup because I want to send the script params as JSON. I haven't figured out any other way to do it eg. datatable.ajax.url().load()

This question has an accepted answers - jump to answer

Answers

  • kthorngrenkthorngren Posts: 21,680Questions: 26Answers: 5,019
    edited February 1

    At a minimum please post your Datatables config so we can see if you are using ajax or loading the data in a different way. Also post your prefilter function so we can see what you are doing and how you re-run the original query.

    Better is a link to a test case replicating the issue so we can help debug.
    https://datatables.net/manual/tech-notes/10#How-to-provide-a-test-case

    Maybe you can use one of the Ajax loaded templates from here to simulate the issue.

    Kevin

  • krytenkryten Posts: 4Questions: 1Answers: 0

    Thanks for the reply

    Here's my table and prefilter function. I set a token refresh timeout to space out refresh queries.
    Set up my params so I receive a single row to rule out any server side problems.

    I don't think I can use a data template as the issue seems to come from the ajax call failing and being retried.

      var table = $('#pt_table').DataTable({
        "ajax": {
          "url": "api/item",
          "contentType": 'application/json',
          "type": 'POST',
          "data": function (d)
          {
            var params = {
              ...
            }
            return JSON.stringify(params);
          }
        },
       ...
       ... // Just column definitions here
      )}
    
        $.ajaxPrefilter(function (opts, originalOpts, jqXHR) 
        {
            var access_token = localStorage.getItem('access_token');
            let tokenRefresh = localStorage.getItem('tokenRefresh');
    
            if (opts.refreshRequest) {
                return;
            }
    
            var data = JSON.stringify({'access_token': access_token});
    
            jqXHR.setRequestHeader("Authorization", "Bearer " + access_token);
    
            var dfd = $.Deferred();
    
            // if the request works, return normally
            jqXHR.done(dfd.resolve);
    
            // if the request fails, do something else
            // yet still resolve
            jqXHR.fail(function (jqXHR, textStatus, errorThrow) {
                var args = Array.prototype.slice.call(arguments);
                let tokenRefresh = localStorage.getItem('tokenRefresh');
                localStorage.setItem('tokenRefresh', Number(tokenRefresh) + 100);
                console.log('Status:', errorThrow, 'Refresh:',localStorage.getItem('tokenRefresh'));
                if (jqXHR.status === 401) {
                    setTimeout(function() {
                        $.ajax({
                            url: 'api/refresh',
                            dataType: 'json',
                            data: data,
                            contentType: 'application/json',
                            method: 'POST',
                            refreshRequest: true,
                            error: function () {
                                dfd.rejectWith(jqXHR, args);
                            },
                            success: function (res) {
    
                                localStorage.setItem('access_token', res.access_token);
                                var newOpts = $.extend({}, originalOpts, { url: opts.url, headers: {"Authorization": "Bearer " + localStorage.getItem('access_token')} });
    
                                // pass this one on to our deferred pass or fail.
                                $.ajax(newOpts).then(dfd.resolve, dfd.reject)
                                localStorage.setItem('tokenRefresh', 0);
                            }
                        });
                    }, tokenRefresh)
    
                } else {
                    dfd.rejectWith(jqXHR, args);
                }
            })
            return dfd.promise(jqXHR);
        });
    
  • kthorngrenkthorngren Posts: 21,680Questions: 26Answers: 5,019
    edited February 1

    Interesting. I took your code and refactored it for this test case:
    https://live.datatables.net/fazuzawi/1/edit

    The ajax option has an invalid URL causing a 404 error. The retry uses the correct URL.

    I did some debugging but I'm not familiar enough with the code to know how all the callbacks are set up. It seems that something is firing twice to call _fnDraw() in datatables.js which is causing the duplicated rows.

    fnDraw() is called once after the error which is the first draw event you see. Then you see the draw event after the successful ajax retry. But the fnDraw() function is called twice at this point. You can see this by putting a breakpoint in datatables.js on the first line of the fnDraw() function.

    @allan will need to take a look to debug the issue.

    Kevin

  • krytenkryten Posts: 4Questions: 1Answers: 0
    edited February 2

    Yep, and it is drawn fine (2 draws not 3 in the example) as far as I can see when you call the refresh manually like with a button

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

    Or if you load the data seperately

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

  • allanallan Posts: 64,015Questions: 1Answers: 10,555 Site admin
    edited February 4 Answer ✓

    Don't call the resolve on the dfd Deferred object on success of the second Ajax call:

    $.ajax(newOpts); //.then(dfd.resolve, dfd.reject)
    

    https://live.datatables.net/fazuzawi/3/edit

    The issue is that the newOpts ajax request does a success call for the DataTables callback function, so then resolving the original one results in it being called twice. Why the data gets passed to the original as well, I couldn't say without digging into the jQuery code. That does seem a bit odd - possibly the json object is shared.

    Allan

  • krytenkryten Posts: 4Questions: 1Answers: 0

    Thanks for the excellent and quick support and for all the work on Datatables.

Sign In or Register to comment.