'Fetching Data from JSON and creating an HTML Table through Javascript - Not Working

I'm trying to create a table populated by an external .JSON file through using JavaScript. It is not working. When the .JSON data was placed in the .JS code it worked, but once I used "fetch" to retrieve data externally it stopped working and does not display the details.



HTML

<input type="button" onclick="CreateTableFromJSON()" value="Create Table From JSON" />
<p id="showData"></p>



JavaScript

fetch('about-us.json')
.then((response) => response.json())
.then((data) => {
    data.forEach(CreateTableFromJSON);
})
.catch((error) => {
    console.error('Error:', error);
});

function CreateTableFromJSON() {

    // EXTRACT VALUE FOR HTML HEADER. 
    // ('Book ID', 'Book Name', 'Category' and 'Price')
    var col = [];
    for (var i = 0; i < myBooks.length; i++) {
        for (var key in myBooks[i]) {
            if (col.indexOf(key) === -1) {
                col.push(key);
            }
        }
    }

    // CREATE DYNAMIC TABLE.
    var table = document.createElement("table");

    // CREATE HTML TABLE HEADER ROW USING THE EXTRACTED HEADERS ABOVE.

    var tr = table.insertRow(-1);                   // TABLE ROW.

    for (var i = 0; i < col.length; i++) {
        var th = document.createElement("th");      // TABLE HEADER.
        th.innerHTML = col[i];
        tr.appendChild(th);
    }

    // ADD JSON DATA TO THE TABLE AS ROWS.
    for (var i = 0; i < myBooks.length; i++) {

        tr = table.insertRow(-1);

        for (var j = 0; j < col.length; j++) {
            var tabCell = tr.insertCell(-1);
            tabCell.innerHTML = myBooks[i][col[j]];
        }
    }

    // FINALLY ADD THE NEWLY CREATED TABLE WITH JSON DATA TO A CONTAINER.
    var divContainer = document.getElementById("showData");
    divContainer.innerHTML = "";
    divContainer.appendChild(table);
}



External .JSON File (about-us.json)

[
{
"Book ID": "1",
"Book Name": "Computer Architecture",
"Category": "Computers",
"Price": "125.60"},
{
"Book ID": "2",
"Book Name": "Asp.Net 4 Blue Book",
"Category": "Programming",
"Price": "56.00"
},
{
"Book ID": "3",
"Book Name": "Popular Science",
"Category": "Science",
"Price": "210.40"
}]


Solution 1:[1]

I had a little time on my hands so I rewrote your code which breaks down the separate steps to make headings, rows, and cells using map and join, and then uses a template string to piece it together. Basically you're just building up small parts of the final HTML as various strings that you combine.

const json='[{"Book ID":"1","Book Name":"Computer Architecture","Category":"Computers","Price":"125.60"},{"Book ID":"2","Book Name":"Asp.Net 4 Blue Book","Category":"Programming","Price":"56.00"},{"Book ID":"3","Book Name":"Popular Science","Category":"Science","Price":"210.40"}]';

// Cache the button, and add an event listener to
// it which calls the API (returns data after two seconds)
const button = document.querySelector('button');
button.addEventListener('click', fetchData, false);

// Mock API
function mockApi() {
  return new Promise(res => {
    setTimeout(() => res(json), 2000);
  });
}

// Calls the API, parses the json, then
// calls createTable with the data
function fetchData() {
  button.textContent = 'Loading...';
  button.disabled = true;
  mockApi()
    .then(res => JSON.parse(res))
    .then(createTable);
}

// `map` over the keys of first object
// in the array, and return a string of HTML
function getColumnHeadings(data) {
  return Object.keys(data[0]).map(key => {
    return `<td>${key}</td>`;
  }).join('');
}

// `map` over each object in the array,
// and return a string of HTML. It calls
// `getCells` on each object
function getRows(data) {
  return data.map(obj => {
    return `<tr>${getCells(obj)}</tr>`
  }).join('');
}

// `map` over the values of an object
// and return a string of HTML
function getCells(obj) {
  return Object.values(obj).map(value => {
    return `<td>${value}</td>`;
  }).join('');
}

function createTable(data) {
  
  // Get the headings, and the rows
  const headings = getColumnHeadings(data);
  const rows = getRows(data);

  // Create an HTML string
  const html = `
    <table>
      <tbody>
        <tr class="headings">${headings}</tr>
        ${rows}
      </tbody>
    </table>
  `

  // Add the string to the DOM
  document.body.insertAdjacentHTML('beforeend', html);

}
table { border-collapse: collapse; border: 1px solid #565656; margin-top: 1em; width: 100%; }
td { padding: 0.6em 0.3em; border: 1px solid #dfdfdf; }
.headings { font-weight: 700; background-color: #dfdfdf; }
button:hover { cursor: pointer; }
<button>Build table</button>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Andy