Search code examples
javascriptjsonajaxdatatable

Universal dataTable adaptable to different data schemas


I have a need to create an ajax dataTable that can adapt to a handful of different data sources. Each data source has different column names and data types. Therefore I cannot have set column names or data types when coding the dataTable. The one thing the various data sources have in common is each has a row_id identity column which is queried on to return one one record.

I have tried many variations of the following but have not yet been successful.

Sample tables in the database:

Table A: enter image description here

Table B: enter image description here

Details HTML:

<table id="tblDetails" class="tbl"></table>

Details Controller:

        public ActionResult GetDetails(int row_id, string table)
        {            
            DataTable dt = new DataTable();
            dt = helper.GetDetails(row_id, table);
            JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
            List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();
            Dictionary<string, object> row;
            if (dt.Rows.Count > 0)
            {
                foreach (DataRow dr in dt.Rows)
                {
                    row = new Dictionary<string, object>();
                    foreach (DataColumn col in dt.Columns)
                    {
                        row.Add(col.ColumnName, dr[col]);
                    }
                    rows.Add(row);
                }
            }
            return Json(rows, JsonRequestBehavior.AllowGet);
        }

js:

    var objParams = { 'table': table, 'row_id': row_id };
    $.ajax({
        url: $("#GetDetailsUrl").val(),
        method: "post",
        data: JSON.stringify(objParams),
        dataType: 'json',
        contentType: "application/json; charset=utf-8",
        success: function (data) {            
            var key = Object.keys(data)[0];
            var value = data[key];
            Object.keys(data).forEach(function (key) {
                console.log(key, value);
                // Everything works fine until the next line of code
                var row = '<tr><td>' + key + '</td><td> ' + value + '</td></tr>';
                $('#tblDetails').append(row);
            })
         }
});

When querying tableB & row_id = 2, the `console.log(key, value);' produces the following console output as expected: enter image description here

However, var row = '<tr><td>' + key + '</td><td> ' + value + '</td></tr>'; fails. It results in key = 0 and value = undefined.

How can I get key & value to work in var row as it does when I output it to the console?


Solution

  • You will need to iterate over each key item in each row object using the Array.forEach(). In the snippet below I simulated your table examples in JSON. I created a function that requires passing in the table node and the table data. Then it creates the table columns no matter how many there are. I commented each line in the function so you can see what it is doing.

    const tableDataA = [{
        row_id: 1,
        customer: `Company 1`,
        name: `Smith`
      },
      {
        row_id: 2,
        customer: `Company 2`,
        name: `Jones`
      },
    ];
    
    const tableDataB = [{
        row_id: 1,
        one: 1,
        two: 2,
        canBeAnything: `XyZ`
      },
      {
        row_id: 1,
        one: 5,
        two: 62,
        canBeAnything: `foo`
      },
    ];
    
    const tblDetailsA = document.querySelector(`#tblDetails-a`);
    const tblDetailsB = document.querySelector(`#tblDetails-b`);
    
    createTable(tblDetailsA, tableDataA);
    createTable(tblDetailsB, tableDataB);
    
    function createTable(tableNode, tableDataArray) {
      // Create the thead element
      const thead = document.createElement('thead');
      // Create the tr for the header
      const headerRow = document.createElement('tr');
      // Get the title for each column by getting 
      // the keys of the first object in the array.
      const keys = Object.keys(tableDataArray[0]);
      // Iterate through each of the keys 
      keys.forEach(key => {
        // Create the th element for the column title
        const th = document.createElement('th');
        // Assign the inner text of the th to the value of the key
        th.innerText = key;
        // Append the the element to the header row
        headerRow.append(th);
      });
      // Append the header row to the thead element
      thead.append(headerRow);
      // Append the thead element to the table
      tableNode.append(thead);
      
      // Create the tbody element
      const tbody = document.createElement('tbody');
      // Iterate though each object of the data array
      tableDataArray.forEach(row => {
        // Create the tr element for the row
        const tr = document.createElement('tr');
        // Iterate through each key in the row Object
        Object.keys(row).forEach(col => {
          // Create the td element for the table data
          const td = document.createElement('td');
          // Assign the inner text of the td
          td.innerText = row[col];
          // Add the td to the row
          tr.append(td);
        });
        // add the row to the tbody
        tbody.append(tr);
      });
      // add the tbody to the table  
      tableNode.append(tbody);
    }
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
    
    <div class="container">
      <h5 class="text-center">Table A</h5>
      <table id="tblDetails-a" class="table"></table>
    </div>
    <div class="container">
      <h5 class="text-center">Table B</h5>
      <table id="tblDetails-b" class="table"></table>
    </div>