0

I have a function that I wish to dynamically generate an HTML table using data that it grabs from an SQL call. As you can see the function receives a groupID and passes it to a function populateDB, which when called will perform an AJAX call to retrieve some data from a DB. I need that data from that AJAX call immediately after the AJAX call before the next lines of code are executed. I know that AJAX is asynchronous and that the rest of the code will execute probably before the data is returned from the AJAX call. I also know that the use of callback functions often help in these situations. I have read this stack post over and over: How do I return the response from an asynchronous call?

However, in this instance, and after hours of problem solving, I cannot see how I can program a callback to work for me here. I am sure I am just not clever enough but I would be so grateful for someone to lend me a hand and show me how I can call the populateDB function and wait for a response, then get that response back into the makeEditTableHTML function for further processing.

        function makeEditTableHTML(studentArray, groupID) { 
                
                populateDB(groupID);

                --- I NEED THE DATA/ARRAY FROM THE AJAX CALL HERE ---
                
                var result = "<table id='dataEditTableid' class='stripe' border=1><thead><tr><td><b>ID</b></td><td><b>Student Email</b></td><td><b>Group ID</b></td><td><b>Target</b></td><td><b>SEN</b></td><td><b>Disadvantaged</b></td></tr></thead>";
                result += "<tbody>";
                for(var i=0; i<studentArray.length; i++) {
                    result += "<tr>";
                    result += "<td>"+studentArray[i][1]+"</td>";
                    result += "<td>"+studentArray[i][0]+"</td>";
                    result += "<td>"+groupID+"</td>";
                    result += "<td id=" + studentArray[i][1] + " contenteditable='true' onBlur='saveToDB(1, this.id, this.innerHTML, "+groupID+")'></td>";
                    result += "<td id=" + studentArray[i][1] + " contenteditable='true' onBlur='saveToDB(2, this.id, this.innerHTML, "+groupID+")'></td>";
                    result += "<td id=" + studentArray[i][1] + " contenteditable='true' onBlur='saveToDB(3, this.id, this.innerHTML, "+groupID+")'></td>";
                    result += "</tr>";
                }
                result += "</tbody></table>";

                return result;
            }

AJAX call function:

        function populateDB(groupID) {
            $(document).ready(function(){   
                $.ajax({
                type: "POST",
                url: {$js_url} + '/wp-content/plugins/WickCustomLD/sqlPopulateDB.php',
                    data: {"groupID" : groupID},
                    success: function(data) {
                        data = JSON.parse(data);
                    },
                })});
        }

Original makeEditTableHTML function call code:

        var result_table = makeEditTableHTML(MultiStudList[groupIndex], groupIDs[groupIndex]);
        dataTable.innerHTML = result_table;
sw123456
  • 3,339
  • 1
  • 24
  • 42
  • When the `makeEditTableHTML` method will be called and how it is getting `studentArray` for the first time? – Sivakumar Tadisetti Jul 17 '20 at 10:15
  • the makeEditTableHTML function is called when the user chooses an item from a dropdown. studentArray is created from a bigger 2D array and the dropdown choice determines an index of that 2D array to pick the subarray, which becomes studentArray. There is an event listener waiting for the change – sw123456 Jul 17 '20 at 12:21
  • so both studentsArray and GroupID are generated after a JS dropdown option is selected. Then they are passed to the makeEditTableHTML function straight after that – sw123456 Jul 17 '20 at 12:25
  • What type of data you will get back as response for ajax call? again students data? or something else? – Sivakumar Tadisetti Jul 17 '20 at 12:34
  • OK, sorry, the returning ajax call will be an array of integers. Basically, I have a drop down list to select a class, this selection generates an array containing class' student's names and their id (studentsArray) and also populates a variable with the group id number (groupID). This array and var are passed to the makeEditTableHTML. Before the table is generated, I want to quickly look up the student IDs (of students that also that groupID) from a custom table of the database. The returning data will be an array of integers - which are student IDs. – sw123456 Jul 17 '20 at 12:43
  • 1
    Makes no sense to have document ready inside of a function call. Second you can not return from an Ajax call. Use the success method to call the step that generates the table with the data – epascarello Jul 17 '20 at 13:03
  • Instead of returning result, you can directly `dataTable.innerHTML = result;` in the `makeEditTableHTML` method itself right? or are you using that result anywhere? – Sivakumar Tadisetti Jul 17 '20 at 13:15
  • 1
    of course! Yes, that has worked! Thank you so much! – sw123456 Jul 17 '20 at 13:19

3 Answers3

1

Refactor your code as follows, pass in a success callback to $.ajax.

function populateDB(groupID, successCb) {
    $(document).ready(function(){   
        $.ajax({
            type: "POST",
            url: {$js_url} + '/wp-content/plugins/WickCustomLD/sqlPopulateDB.php',
            data: {"groupID" : groupID},
            success: successCb
        });             
    });
}
        
function makeEditTableHTML(groupID, studentArray, successCb) { 
    return populateDB(groupID, function(data) {
        // Data from AJAX POST
        console.log(data);
        
        var result = "<table id='dataEditTableid' class='stripe' border=1><thead><tr><td><b>ID</b></td><td><b>Student Email</b></td><td><b>Group ID</b></td><td><b>Target</b></td><td><b>SEN</b></td><td><b>Disadvantaged</b></td></tr></thead>";
        result += "<tbody>";
        
        for(var i=0; i < studentArray.length; i++) {
            result += "<tr>";
            result += "<td>"+studentArray[i][1]+"</td>";
            result += "<td>"+studentArray[i][0]+"</td>";
            result += "<td>"+groupID+"</td>";
            result += "<td id=" + studentArray[i][1] + " contenteditable='true' onBlur='saveToDB(1, this.id, this.innerHTML, "+groupID+")'></td>";
            result += "<td id=" + studentArray[i][1] + " contenteditable='true' onBlur='saveToDB(2, this.id, this.innerHTML, "+groupID+")'></td>";
            result += "<td id=" + studentArray[i][1] + " contenteditable='true' onBlur='saveToDB(3, this.id, this.innerHTML, "+groupID+")'></td>";
            result += "</tr>";
        }
        
        result += "</tbody></table>";
        return successCb(result);
    }); 
}

// In the calling function
makeEditTableHTML(MultiStudList[groupIndex], groupIDs[groupIndex], function(result_table) {
    dataTable.innerHTML = result_table;
});
Kunal Mukherjee
  • 5,775
  • 3
  • 25
  • 53
  • Thank you very much for helping. Unfortunately, when I run this code, the table no longer generates and instead i get undefined. Any idea why? – sw123456 Jul 17 '20 at 12:19
  • Thank you again for your help. The table still does not generate, instead I get undefined. However, the console.log(data) correctly displays the array returned from the AJAX call, so the data is being collected in the right place! :) – sw123456 Jul 17 '20 at 12:54
  • @sw123456 The table does not generate because there is no statement to set it to the DOM element, you have just created the HTML string but not set to the appropriate DOM element – Kunal Mukherjee Jul 17 '20 at 12:55
  • Maybe I should have shown this before. When I call the makeEditTableHTML function from elsewhere, the returning data is then used to update a table element. I will update my question with this code. – sw123456 Jul 17 '20 at 12:59
  • post the calling function of `makeEditTableHTML` – Kunal Mukherjee Jul 17 '20 at 13:00
  • I'm not sure why the result was not being returned properly, but as suggested in the comments above, I have added the table update code in the makeEditTableHTML function and it now renders. – sw123456 Jul 17 '20 at 13:20
  • @sw123456 I have updated my code for your updated question – Kunal Mukherjee Jul 17 '20 at 13:29
  • @sw123456 you can't return any value from any asynchronous function, you have to synchronize your code in such a way that you schedule the code to run once the result from the asynchronous function is available – Kunal Mukherjee Jul 17 '20 at 14:35
1

We can't simply return if we are working with async requests, instead of return we should append data in HTTML DOM.

In your case, you are getting undefined because when you try to generate table, data was not there or i should say AJAX call not completed yet. And when AJAX call completed, DOM has completed its working. now we have to update the DOM like i have done using innerHTML

function loadUserData() {
            var xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = function () {
                if (this.readyState == 4 && this.status == 200) {
                    renderTable(JSON.parse(this.responseText));
                }
            };
            xhttp.open("GET", "https://jsonplaceholder.typicode.com/users", true);
            xhttp.send();
        }

        function renderTable(studentArray){
                    var result = "<table border=1><thead><tr><td><b>ID</b></td><td><b>Email</b></td><td><b>Username</b></td><td><b>Name</b></td><td><b>City</b></td></tr></thead>";
                    result += "<tbody>";

                    for (var i = 0; i < studentArray.length; i++) {
                        result += "<tr>";
                        result += "<td>" + studentArray[i]['id'] + "</td>";
                        result += "<td>" + studentArray[i]['email'] + "</td>";
                        result += "<td>" + studentArray[i]['username'] + "</td>";
                        result += "<td>" + studentArray[i]['name'] + "</td>";
                        result += "<td>" + studentArray[i]['address']['city'] + "</td>";
                        result += "</tr>";
                    }

                    result += "</tbody></table>";
                    document.getElementById("demo").innerHTML = result
        }
<!DOCTYPE html>
<html>

<body>

    <h2>Generate Table for User Data</h2>

    <button type="button" onclick="loadUserData()">Load User Data</button>

    <p id="demo"></p>

</body>

</html>
Vinesh Goyal
  • 607
  • 5
  • 8
1

If you like to experiment, try below suggestion too, first make the Ajax call and in the success callback, construct the table. And remove document.ready() while making Ajax call as it is not required.

Instead of calling makeEditTableHTML, call populateDB method on dropdown item changed.

function populateDB(studentArray, groupID) {
    $.ajax({
      type: "POST",
      url: {
        $js_url + '/wp-content/plugins/WickCustomLD/sqlPopulateDB.php',
      }
      data: {
        "groupID": groupID
      },
      success: function(data) {
        makeEditTableHTML(groupID, studentArray, data);
      },
    });
}

function makeEditTableHTML(groupID, studentArray, data = []) { // Provide default value to **data** parameter
  console.log(data);
  var result = "<table id='dataEditTableid' class='stripe' border=1><thead><tr><td><b>ID</b></td><td><b>Student Email</b></td><td><b>Group ID</b></td><td><b>Target</b></td><td><b>SEN</b></td><td><b>Disadvantaged</b></td></tr></thead>";
  result += "<tbody>";
  for (var i = 0; i < studentArray.length; i++) {
    result += "<tr>";
    result += "<td>" + studentArray[i][1] + "</td>";
    result += "<td>" + studentArray[i][0] + "</td>";
    result += "<td>" + groupID + "</td>";
    result += "<td id=" + studentArray[i][1] + " contenteditable='true' onBlur='saveToDB(1, this.id, this.innerHTML, " + groupID + ")'></td>";
    result += "<td id=" + studentArray[i][1] + " contenteditable='true' onBlur='saveToDB(2, this.id, this.innerHTML, " + groupID + ")'></td>";
    result += "<td id=" + studentArray[i][1] + " contenteditable='true' onBlur='saveToDB(3, this.id, this.innerHTML, " + groupID + ")'></td>";
    result += "</tr>";
  }
  result += "</tbody></table>";

  dataTable.innerHTML = result_table;
}
Sivakumar Tadisetti
  • 4,865
  • 7
  • 34
  • 56