0

I'm still very new to JavaScript, but the basic premise of my program is to make an API call, and then turn that data into a table. I have tested the buildHtmlTable function and it works fine with a sample array that I pre-populated with static data (not from API).

In the buildHtmlTable function console.log(myList.length) returns 0. This is most likely where to problem stems from because if length is 0 then for (var i = 0; i < myList.length; i++ does not run at all.

I've also tried adding data to my table using .push and seem to get the same errors.

Here is what my code looks like:

<body onLoad="buildHtmlTable('#excelDataTable')">
  <table id="excelDataTable" border="1">
  </table>
</body>

<script>

  var myList = [];


  function getAPIData() {
    // Create a request variable and assign a new XMLHttpRequest object to it.
    var request = new XMLHttpRequest()


    // Open a new connection, using the GET request on the URL endpoint
    request.open('GET', '/api/table=1/records/', true)
    request.onload = function () {
      // Begin accessing JSON data here
      var data = JSON.parse(this.response)

      n = 0

      if (request.status >= 200 && request.status < 400) {
        data.forEach(record => {
          myList[n] = (record.data);
          n++;
          //console.log(record.data.name)
        })
      } else {
        console.log('error')
      }
    }

    request.send()
    console.log('fin')
  }



  // Builds the HTML Table out of myList.
  function buildHtmlTable(selector) {
    getAPIData()
    console.log(myList.length)

    console.log(1)

    var columns = addAllColumnHeaders(myList, selector);

    console.log(1.1)
    console.log(myList.length)

    for (var i = 0; i < myList.length; i++) {
      console.log(1.2)

      var row$ = $('<tr/>');
      console.log(1.3)

      for (var colIndex = 0; colIndex < columns.length; colIndex++) {
        var cellValue = myList[i][columns[colIndex]];
        if (cellValue == null) cellValue = "";
        row$.append($('<td/>').html(cellValue));
      }
      $(selector).append(row$);
    }

    console.log(2)

  }

  // Adds a header row to the table and returns the set of columns.
  // Need to do union of keys from all records as some records may not contain
  // all records.
  function addAllColumnHeaders(myList, selector) {
    var columnSet = [];
    var headerTr$ = $('<tr/>');

    for (var i = 0; i < myList.length; i++) {
      var rowHash = myList[i];
      for (var key in rowHash) {
        if ($.inArray(key, columnSet) == -1) {
          columnSet.push(key);
          headerTr$.append($('<th/>').html(key));
        }
      }
    }
    $(selector).append(headerTr$);

    return columnSet;
  }
</script>

As for the requested data, this is what the returned JSON looks like:

[
    {
        "id": 1,
        "data": {
            "name": "John Doe",
            "id": "5d7861f38319f297df433ae1"
        }
    },
    {
        "id": 2,
        "data": {
            "name": "John deer",
            "id": "5d7861f38319f297df433ae1"
        }
    },
    {
        "id": 3,
        "data": {
            "name": "Jane Doe",
            "id": "5d79126f48ca13121d673300"
        }
    }
]

Any idea on where I am going wrong here? Thanks.

Edit: Here is what my implementation using fetch. However, I'm still getting an array with a length of 0.

  async function getAPIData() {
    const response = await fetch('/api/table=1/records/')
    const myJson = await response.json();
    myJson.forEach(record => {
      myList.push(record.data);
    })
  }
ng150716
  • 2,195
  • 5
  • 40
  • 61

1 Answers1

1

XMLHttpRequests are asynchronous, meaning the rest of your code won't wait for them to finish before they run. When you call getAPIData(), it starts making the request, but then it goes to the next line of buildHtmlTable before the request is complete (and thus before the list is populated). What you should be doing is calling the getAPIData function outside the buildHtmlTable function, then calling buildHtmlTable in the onload callback of the XHR request. This will ensure the data is loaded and populated by the time the HTML building function is run.

You could also switch to using fetch instead of XMLHttpRequest; since fetch returns a promise, you can use the ES6 async / await syntax to just await the API response before the code inside the buildHtmlTable function continues. But that's a new way to think about AJAX and asynchronous behavior, so if you're not used to it, I'd say stick to my first suggestion instead.

IceMetalPunk
  • 5,476
  • 3
  • 19
  • 26
  • Thanks for the response. I was able to quickly build a function using fetch, but am still facing the same problems. I've gone ahead and updated my question to reflect the new function. Mind taking a look? Thanks – ng150716 Sep 24 '19 at 20:20
  • 1
    That's a good start. Now you'll need to make buildHtmlTable an async function and have it await getAPIData() as well. – IceMetalPunk Sep 24 '19 at 20:34