Confused about when ajax.dataSrc is executed

Confused about when ajax.dataSrc is executed

CarnenoCarneno Posts: 36Questions: 7Answers: 0
edited June 2017 in Free community support

Hello,

I have an ASP.NET, MVC 5, .Net 4.5, C#, JavaScript, Jquery Datatables web application developed in Visual Studio 2017 Community.

It allows a user to select a datatables mailbox(masterTable) entry and when the user clicks a download button, it gets all of the emails and displays them in another datatables email(detail) table on the same screen. After the emails are downloaded, it displays the date and time it downloaded in the CommunicationStatus column of the masterTable. It does do that.

I am having a problem, when I click the download button the first time, it displays a blank in the CommunicationStatus column of the masterTable. The next time I click the download button, it displays the date and time from the previous download in the CommunicationStatus column. The same with successive downloads, it always displays the previous download status in the CommunicationStatus column.

I started to setup a live.datatables.net test, but after thinking about it, I can't really allow a test without putting in live mailbox data with valid email usernames and passwords. I don't want to do that and I think you know why.

I did put all of my HTML and Javascript code there: live.datatables.net/xohosila/4/edit?html,css,js,console,output and I can tell you that the code for the download button click is this:

                    $('.email-download').click(function () {
                         //alert("made it to email download");
                         masterTable.rows().every(function (rowIdx, tableLoop, rowLoop) {
                              if ($(this.node()).hasClass('selected')) {
                                   var data = this.data();
                                   currentrow = masterTable.row(rowIdx).index();
                                   //masterTable.cell(currentrow, 7).data("Processing");
                                   masterTable.cell(rowIdx, 7).data("Processing");
                                   selected = data.MailboxID;
                                   $('#detail').show();
                                   $('#detail').DataTable(detailsTableOpt).draw();
                                   masterTable.cell(rowIdx, 5).data(mails);
                                   masterTable.cell(rowIdx, 7).data(comm_status);
                                   comm_status = "";
                                   mails = 0;
                              };
                         });
                    });

.
I took it through the F12 debugger and I noticed that it executes all of the above code before executing this:

                              "dataSrc": function (json) {
                                   comm_status = json.data.queryStatus;
                                   mails = json.recordsTotal;
                                   return json.data;
                                   }

.
In the #detail datatable. That's not consistent with the way I interpreted this: https://datatables.net/reference/option/ajax.dataSrc

Does anyone know how I can get the "dataSrc": function (json) to execute before it returns to after the $('#detail').DataTable(detailsTableOpt).draw(); statement?

Any help that anyone can provide to resolve this problem would be gratefully appreciated.

Thanks,
Tony

Answers

  • bindridbindrid Posts: 730Questions: 0Answers: 119

    It is not intended to execute. Its purpose is to tell DataTables where, in the ajax response where the data is in the response object.

    for example DataTables is expecting something like {data:[your data here]} but for some reason, your server is returning [your data here], you would set dataSrc: "".

    If your server is return {StudentList:[your data]} then dataSrc: "StudentList"

  • CarnenoCarneno Posts: 36Questions: 7Answers: 0

    bindrid,

    I appreciate your help.

    If it is not intended to execute, then why does it stop there in the debugger when I set a breakpoint?

    Maybe my idea of "execute" is different than what other people would explain it to be.

    I'm still puzzled over this, since it does give me the status field. It just gives it to me after the draw and the rest of the code is executed.

    Thanks,
    Tony

  • bindridbindrid Posts: 730Questions: 0Answers: 119

    After some further reading, I am backing off my answer a bit.

    In fact

    "dataSrc": function (json) {
         return json.data;
      }
    

    and

    "dataSrc":"data", 
    

    accomplish the exact same thing. Sorry if I wasted your time.

  • bindridbindrid Posts: 730Questions: 0Answers: 119

    Ok, I came of with something a little different. I pulled ajax out of the detail table and removed serverSide: true.
    I create an ajax function that does an update to the master table after the ajax completes, then it updates the details.
    This logic has not been tested but it should give you an idea what I am trying to do.

    $('.email-download').click(function () {
        //alert("made it to email download");
        masterTable.rows().every(function (rowIdx, tableLoop, rowLoop) {
            if ($(this.node()).hasClass('selected')) {
                var data = this.data();
                currentrow = masterTable.row(rowIdx).index();
                //masterTable.cell(currentrow, 7).data("Processing");
                masterTable.cell(rowIdx, 7).data("Processing");
                selected = data.MailboxID;
              //  $('#detail').show();
                //  $('#detail').DataTable(detailsTableOpt).draw();
                doAjax(selected).done(function (json) {
                    var comm_status = json.data.queryStatus;
                    var mails = json.recordsTotal;
                    masterTable.cell(rowIdx, 5).data(mails);
                    masterTable.cell(rowIdx, 7).data(comm_status);
                    $('#detail').clear();
                    $('#detail').rows.add(json.data);
                    $('#detail').draw();
                });
            };
        });
    });
        /**
         * This function simplifys the ajax call and uses a defered object to return the results
         */
    function doAjax(param) {
        var deferred = new $.Deferred();
        $.ajax({
            url:'http://tonygirgenti.biz/Mailbox/GetEmailsData',
            'data': function (d) {
                d.MailboxID = selected;
            },
            success: function (response) {
                deferred.resolve(response);
            }
        })
        return deferred.promise();
    }
    
  • CarnenoCarneno Posts: 36Questions: 7Answers: 0

    bindrid,

    Thanks for doing this.

    Not knowing what the $.Deferred object does, I tried using your code anyway.

    I'm guessing you are trying to delay any processing until the ajax call is completed.

    Using your code, it doesn't display the detail table at all. I'm not sure of why. I would need to study what your code does to figure out why it is not working.

    I would also like to try $.When callback, but I don't know how to code it.
    api.jquery.com/jQuery.when/
    https://stackoverflow.com/questions/35191456/wait-until-three-ajax-calls-have-resolved-to-fire-function-defer

    Thanks,
    Tony

  • allanallan Posts: 61,453Questions: 1Answers: 10,055 Site admin

    The ajax.dataSrc option is actually not suitable for use with serverSide.

    The ajax.dataSrc option tells DataTables where to get the data array to display in the table. However, looking at your example code you are also using it to get other parameters and then returning the array. But server-side processing needs more than an array - it needs an object with certain properties.

    Instead of using ajax.dataSrc as you are, listen for the xhr event. You'll get the same information available, it will just work overall better.

    Being able to use ajax.dataSrc with server-side processing is something that is on my to-do list!

    Allan

  • CarnenoCarneno Posts: 36Questions: 7Answers: 0

    allan,

    Thanks for your help.

    I tried this:

                        $('#detail').on('xhr.dt', function (e, settings, json, xhr) {
                             if (json == null) {
                                  alert("XHR Error")
                                  comm_status = null;
                                  mails = 0;
                             }
                             else {
                                  comm_status = json.queryStatus;
                                  mails = json.recordsTotal;
                             };
                        });
    

    .
    It still does the same thing.

    Also, I used the F12 debugger and it behaves the same as the "dataSrc": function (json), it is always one update behind. It executes all of the statements in the $('.email-download').click(function () before it executes the xhr.dt.

    Thanks,
    Tony

  • CarnenoCarneno Posts: 36Questions: 7Answers: 0

    I want to try this:

                             "drawCallback": function (settings) {
                                  var api = this.api();
                                  // Output the data for the visible rows to the browser's console
                                  //console.log(this.row().data());
                                 
                             },
    
    

    .
    But I don't know how to get the data returned by the call to ajax.

  • allanallan Posts: 61,453Questions: 1Answers: 10,055 Site admin

    Try this.api().ajax.json() in the drawCallback to get the JSON data.

    It executes all of the statements in the $('.email-download').click(function () before it executes the xhr.dt.

    Assuming that the click event is what triggers the Ajax request, then yes, that ordering would be correct.

    Allan

  • CarnenoCarneno Posts: 36Questions: 7Answers: 0

    allan,

    Thanks for your help. I am able to get the data I need using your statement.

    However, I still have the same problem with drawCallback. It executes all of the statements in the masterTable.rows().every(function for each selected row before it does the drawCallback.

    Do you have any idea of how I can get it to do the drawCallback for each selected row after it executes the $('#detail').DataTable(detailsTableOpt).draw(); statement?

    If I use the F12 debugger and three masterTable rows are selected, it goes through the every loop three times before it does the drawCallback three times.

    I would like it to update the masterTable cells for each row that executes the $('#detail').DataTable(detailsTableOpt).draw(); statement at the time that it executes that statement.

    Any help that you can provide to accomplish that would be gratefully appreciated.

    Thanks,
    Tony

  • allanallan Posts: 61,453Questions: 1Answers: 10,055 Site admin

    I'm not quiet getting it I'm afraid - although I think that's my understanding of what you are attempting to do. You are making an Ajax call for every row in the table - let's say it has 100 rows, that means 100 Ajax requests which is going to quickly DDoS your own server if you have more than a couple of users!

    You would really want to combine those Ajax requests into a single request to the server.

    You also have a closure issue with the code above - since the Ajax request is async the loop will continue to execute (triggering more Ajax requests) while the first one is in progress. The result is that rowIdx will not be the value to expect in your callback. You could make it a sync request, but that will kill performance.

    I'd really recommend you have a think about making a single request to the server - it will be far more efficient and you won't run into these async / closure issues.

    Allan

  • CarnenoCarneno Posts: 36Questions: 7Answers: 0

    allan,

    Thanks for your suggestion. What you say makes sense. I will attempt to combine those Ajax requests into a single request to the server.

    It means I will have to change the server side processing. Part of the problem in doing that is that the MailKit client will not allow multiple mailbox calls. I will have to create a loop to make client calls for each mailbox.

    I will still need a way to update the masterTable with results of each mailbox call to MailKit client. The only way I can see to do that is to append an array of results from the server when returning the JSON results for the DataTables detail table.

    Thanks,
    Tony

  • allanallan Posts: 61,453Questions: 1Answers: 10,055 Site admin

    That probably is the way to do it.

    Can you show me a screenshot of your page so I can visualise it?

    Allan

  • CarnenoCarneno Posts: 36Questions: 7Answers: 0

    This is what my screen looks like:

    https://drive.google.com/open?id=0B3Jw7h2K4ZvkWmJoSUM4ZDR5LU0

    The columns in the masterTable I want to update upon return from server are "Mail" and "Status".

    The Mailbox column in the detail table is the Alias column in the masterTable.

    The detail table only displays emails for mailboxes selected in masterTable.

    Thanks,
    Tony

  • allanallan Posts: 61,453Questions: 1Answers: 10,055 Site admin

    Thanks for the screenshot. I don't see the .email-download button / link. Where is that? I'm not clear if that's in the master table for the child table.

    Allan

  • CarnenoCarneno Posts: 36Questions: 7Answers: 0

    allan,

    The download button is the red button next to the Add button.

    Thanks,
    Tony

  • allanallan Posts: 61,453Questions: 1Answers: 10,055 Site admin

    I see - thanks!

    So you basically seem to have two options:

    1. Keep it client-side and have it make an Ajax request for every row that is selected, adding those items to the table below as they are returned. Clear the table before you make any Ajax calls and simply call a function which will make an Ajax request for the data, passing in the table and the row information required to get the child rows for it. That function will take care of the closure issues I mentioned above.
    2. Do something on the server-side to abstract away the multiple request requirement so you can, from the client-side, make a single request to get the data and that server-side script will handle the multiple requests.

    Allan

This discussion has been closed.