简体   繁体   中英

Build dynamic table from JSON

I've been stuck trying to build this dynamic table for the past couple of days. I've built it out several different ways and have finally gotten it to a point where I have the correct output, however the work I've done is manual. I am hoping someone could help me make this be more dynamic.

here's an example of my JSON (super simplified)

var obj1 = {
"Summary" : 
[
{
    "ID" : "1234", 
    "Name" : "John", 
    "Status" : "Green",
},
{
    "ID" : "5678", 
    "Name" : "Mike", 
    "Status" : "Green",
},
{
    "ID" : "9012", 
    "Name" : "Tom", 
    "Status" : "Red",
},
{
    "ID" : "3456", 
    "Name" : "Chris", 
    "Status" : "Red",
},
{
    "ID" : "2445", 
    "Name" : "Pat", 
    "Status" : "Green",
},
{
    "ID" : "6543", 
    "Name" : "James", 
    "Status" : "Red",
}

]
};

I need the output to look something like this (which it is), however I may have more than 6 objects in my array, so I need to iterate through this rather than build it out by hand.

1234   5678    9012    3456    2445    6543
John   Mike     Tom    Chris   Pat     James
Green  Green   Red     Red     Green   Green

Here's my code thus far. Any help would be much appreciated.

for (j in obj1.Summary[0]) {
document.write('<tr><td class="' + j +'">' + j + '</td><td class="' + j +'">' +     obj1.Summary[0][j] + '</td><td class="' + j +'">' + obj1.Summary[1][j] + '</td><td class="' + j +'">' + obj1.Summary[2][j] + '</td><td class="' + j +'">' + obj1.Summary[3][j] + '</td><td class="' + j +'">' + obj1.Summary[4][j] + '</td><td class="' + j +'">' + obj1.Summary[5][j] + '</td></tr>');
}

I recently came up with an interesting pattern for creating tables from database output dynamically, while holding references to the relevant created elements and the values used to create them.

This method came in handy for me because I created input elements for each table cell, then utilized the structure created to check the original values against their actual values and generate an sql update query based on the changed fields.

I realize that it may be overkill for this specific situation, but it does aid in readability and maintainability so I'm posting it here in case it could possibly help someone else in the future.

 var responseText = {"Summary":[{"ID":"1234","Name":"John","Status":"Green",}, {"ID":"5678","Name":"Mike","Status":"Green",}, {"ID":"9012","Name":"Tom","Status":"Red",}, {"ID":"3456","Name":"Chris","Status":"Red",}, {"ID":"2445","Name":"Pat","Status":"Green",}, {"ID":"6543","Name":"James","Status":"Red",}]}; var list = new List(responseText.Summary); document.body.appendChild(list.node); function List(data) { if(!(this instanceof List)) return false; var list = this.node = document.createElement('div'); list.setAttribute('class','list'); var items = this.items = {}; for(var i in data) { items[i] = new ListItem(data[i]) list.appendChild(items[i].node); } } function ListItem(data) { if(!(this instanceof ListItem)) return false; var item = this.node = document.createElement('div'); item.setAttribute('class','item'); var lines = this.lines = {}; for(var i in data) { lines[i] = new ListItemLine(i, data[i]) item.appendChild(lines[i].node); } } function ListItemLine(name, value) { if(!(this instanceof ListItemLine)) return false; var line = this.node = document.createElement('div'); this.name = name; this.value = value; line.setAttribute('class','line ' + name); if(name !== 'ID') line.setAttribute('contenteditable', true); line.appendChild(document.createTextNode(value)); } 
 .item { display: inline-block; padding: 5px; text-align: center; } 

Then I used something similar to this inside the List class,

list.onkeydown = function(e) {
    if(e.which !== 13) return true;
    e.preventDefault();
    var changes = [];
    for(var i in items) (function(item, data){
        for(var i in data.lines) (function(line){
            var value = line.value, 
                actual = line.node.textContent;
            if(value !== actual) changes.push({
                id: data.lines['ID'].value,
                name: line.name,
                value: actual
            });
        })(data.lines[i]);
    })(i, items[i]);
    console.log(encodeURIComponent(JSON.stringify(changes)));
}

Where instead of using console.log, I send the data via ajax to a receiver page that generates update sql and returns the result of the query. Of course there are many methods of doing the last part of this, this is the one that was the most useful for me.

When you want to repeat logic, you should use a loop. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for

for (var i=0; i < obj.Summary.length; i++) {
  var object = obj.Summary[i]
  // write to document
}

You should either transpose your data, or change your interface as well.

1234  John  Green
5678  Mike  Green

You may find a rendering library useful as well, to avoid messing with string concatenation.

https://github.com/leonidas/transparency

Edit

No problem, still use loops. Build the rows in the loop and concatenate them together. See the mdn array docs especially forEach and join .

// specify keys and init rows

var keys = [ "ID" ] 
var rows = {}

keys.forEach(function (key) {
  rows[key] = []
})

// ok now we have rows

console.log(rows)

// add table cells to rows

summaryObjects.forEach(function (object) {
  for (var key in object) {
    var cell = "<td>" + object[key] + "</td>"
    rows[key].push(cell)
  }
})

// now we have cells in the rows

console.log(rows)

// put together the table

keys.forEach(function (key) {
  document.write("<tr>" + rows[key].join('') + "</tr>")
})

That's what i mean by transpose above, like a matrix transpose in linear algebra. Your data looks like this:

[
  { key: value, key: value }
  { key: value, key: value }
]

And you want

{
  key: [ value, value ],
  key: [ value, value ]
}

Make a variable and put the text in there. That way you can build it using nested loops then insert it in the document. I just did something similar in PHP that can take a db table as nested arrays and generate a table from it.

var table = "<table>";

//we loop over the attributes since you want that format
for (userAttribute in obj1.Summary[0]) {
  //these are your headers/titles
  table += '<tr><th>' + userAttribute + '</th>';

  //and here we build a row getting the attribute from each user
  for (userIndex in obj1.Summary) {
    var user = obj1.Summary[userIndex];
    table += '<td>' + user[userAttribute] + '</td>';
  }  
  table += '</tr>'; //close that row and move on to the next attribute
}

//close out the table
table += '</table>';

document.write(table);

I would suggest using template such as Handlebarsjs to do this. This way, you keep the html separated from JavaScript and you don't have to add so many '+'.

For example, you can embed this on your html.

<script id="template" type="text/x-handlebars-template">

    {{#each Summary}}

        <tr><td>{{this.ID}}</td>
            <td>{{this.Name}}</td>
            <td>{{this.Status}}</td>
        </tr>

    {{/each}}
</script>

I think I messed up the tags but you get the idea.

Then on the script file, you can compile the template and get the data. More info on http://handlebarsjs.com/

var PROJECT_METHOD ={

            handlerData:function(resJSON){

                var templateSource   = $("#template").html(),

                    template = Handlebars.compile(templateSource),

                    projectHTML = template(resJSON),

                    parser = new DOMParser(),

                    // the result
                    doc = parser.parseFromString(projectHTML, "text/html");
                    document.write(doc);

            },
            loadProjectData : function(){

                $.ajax({
                    url:"data.json",
                    method:'get',
                    success:this.handlerData
                });
            }
         };

         PROJECT_METHOD.loadProjectData();

Hope it helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM