简体   繁体   English

Javascript从json创建复杂的表头

[英]Javascript Create complex table header from json

I want to create a html table header with colspan and i'm lost; 我想用colspan创建一个html表头,我迷路了; i have this json: 我有这个json:

var metadata = [{
"colIndex": 0,
    "colType": "String",
    "colName": "PM"
}, {
"colIndex": 1,
    "colType": "String",
    "colName": "PROD"
}, {
"colIndex": 2,
    "colType": "Numeric",
    "colName": "NR/M1"
}, {
"colIndex": 3,
    "colType": "Numeric",
    "colName": "NR/M2"
}, {
"colIndex": 4,
    "colType": "Numeric",
    "colName": "NR/M3"
}, {
"colIndex": 5,
    "colType": "Numeric",
    "colName": "NR/M4"
}, {
"colIndex": 6,
    "colType": "Numeric",
    "colName": "NR/M5"
}, {
"colIndex": 7,
    "colType": "Numeric",
    "colName": "NR ART/M6"
}, {
"colIndex": 8,
    "colType": "Numeric",
    "colName": "NR ART/M1"
}, {
"colIndex": 9,
    "colType": "Numeric",
    "colName": "NR ART/M2"
}, {
"colIndex": 10,
    "colType": "Numeric",
    "colName": "NR ART/M3"
}, {
"colIndex": 11,
    "colType": "Numeric",
    "colName": "NR ART/MX"
}];

The table header will be, mostly, based on spliting "colType": "Numeric" 该表头主要基于拆分“ colType”:“ Numeric”

+------+-------+-----+--------+-----+-----+-----+-----+------+----+----+----+
|      |       |               NR               |           NR ART          |
+ PAM  | PROD  +-----+--------+-----+-----+-----+-----+------+----+----+----+
|              | M1   | M2    | M3  | M4  | M5  | M6  | M1  | M2  | M3 | MX |
+------+-------+-----+--------+-----+-----+-----+-----+------+----+----+----+

First i tried to split colName where colType is numeric 首先我尝试在colType为数字的情况下拆分colName

var arr = [], a;
$.each(metadata, function (i, v) {
  arr.push(v.colName.split("/"));
  a = v.colName.split("/").length;
});

next, i get unique parents (?), but what can i do with this? 接下来,我得到了唯一的父母(?),但是我该怎么办? i think i must travers this array in hierarchie (parent - children and then to construct the html header). 我认为我必须遍历此数组(父级-子级,然后构造html标头)。

The array is dynamic. 该数组是动态的。

Any advice? 有什么建议吗? Please and thank you. 谢谢,麻烦您了。

the below code should do the work please check 下面的代码应该做的工作,请检查

var arr = {}, a;
$.each(metadata, function (i, v) {
     arr[v.colName.split("/")[0]] = arr[v.colName.split("/")[0]].push(v.colName.split("/")[1]) || [] ;       
});

In order to display it 为了显示它

for(t1 in arr){
    if(t1.length > 0){
          //has children element
    }
    else{
        //has only 1 element
    }
}

One thing which complicates this, is the need for colspan and rowspan in your table. 使此问题复杂化的一件事是表中需要colspanrowspan So, you need to do a group count on your objects based on the colName property. 因此,您需要基于colName属性对对象进行组计数。

Here is a crude example of how you could do it. 这是如何实现的粗略示例。 Explanation is in the code comments. 说明在代码注释中。 (This example uses group count for colspan , but rowspan is hard-coded. You can do a group-count for rowspan as well). (此示例对colspan使用组计数,但对rowspan进行硬编码。您也可以对rowspan进行组计数)。

I have used jQuery in this example only for creating and adding the table. 在此示例中,我仅将jQuery用于创建和添加表。 The core logic is in plain Javascript . 核心逻辑使用纯Javascript

Snippet : 片段

 var metadata = [{"colIndex": 0, "colType": "String", "colName": "PM"}, {"colIndex": 1, "colType": "String", "colName": "PROD"}, {"colIndex": 2, "colType": "Numeric", "colName": "NR/M1"}, {"colIndex": 3, "colType": "Numeric", "colName": "NR/M2"}, {"colIndex": 4, "colType": "Numeric", "colName": "NR/M3"}, {"colIndex": 5, "colType": "Numeric", "colName": "NR/M4"}, {"colIndex": 6, "colType": "Numeric", "colName": "NR/M5"}, {"colIndex": 7, "colType": "Numeric", "colName": "NR ART/M6"}, {"colIndex": 8, "colType": "Numeric", "colName": "NR ART/M1"}, {"colIndex": 9, "colType": "Numeric", "colName": "NR ART/M2"}, {"colIndex": 10, "colType": "Numeric", "colName": "NR ART/M3"}, {"colIndex": 11, "colType": "Numeric", "colName": "NR ART/MX"}]; // First filtering objects which will form the headers var rowHeaders = metadata.filter(function(obj) { return obj.colType == "String"; }); var colHeaders = metadata.filter(function(obj) { return obj.colType == "Numeric"; }); // Now we need the grouping on colName with value before slash. // These will form the group header. Count will determine the colspan. var colHeadersMain = {}; metadata.forEach(function(obj) { if (obj.colType == "String") { return } // Only for Numeric colType // Check if the key has been stored in the colHeadersMain if (obj.colName.split('/')[0] in colHeadersMain) { // If yes, then increment the value of the key for object colHeadersMain colHeadersMain[obj.colName.split('/')[0]]++; } else { // If not, reset the value colHeadersMain[obj.colName.split('/')[0]] = 1; } }); // Variables to hold jQuery elements var $table = $("<table>"), $thead = $("<thead>"), $row1 = $("<tr>"), $row2 = $("<tr>"); // Iterate row headers and form the first row // Hard-coding the rowspan. // ToDo: Maintain count, similar to column headers. rowHeaders.forEach(function(obj, idx) { var $th = $("<th rowspan='2'>"); $th.text(obj.colName); $row1.append($th); }); // Iterate col grouping headers and continue the row // colpsan is the group count that we created earlier for (var key in colHeadersMain) { var $th = $("<th colspan='" + colHeadersMain[key] + "'>"); $th.text(key); $row1.append($th); } // Add the first header row to thead $thead.append($row1); // Iterate next line of col headers colHeaders.forEach(function(obj, idx) { var $th = $("<th>"); $th.text(obj.colName.split('/')[1]); $row2.append($th); }); // Add to the thead $thead.append($row2); // Add to the table $table.append($thead); // Add to the page / element $("#wrap").append($table); // Done. 
 table, td, th { border: 1px solid gray; border-collapse: collapse; } th, td { padding: 4px; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="wrap"></div> 

As you did not tag jQuery in your post, here's a plain-vanilla JavaScript solution (although you did use a $ in the code of your post, which could indicate jQuery). 由于您没有在帖子中标记jQuery,因此这是一个普通的JavaScript解决方案(尽管您确实在帖子代码中使用了$ ,这可能表示jQuery)。

The code below, when applied to the metadata will parse it into a format that's a little more conducive to what you want to do: 下面的代码应用于metadata会将其解析为一种更有利于您要做的事情的格式:

var columns = [];
metadata.forEach(function(column, index, array) { // parse into a more friendly format for making columns
    var col = {}, // will be the column
        parts = column.colName.split('/'), // you're splitting on the column name in your post
        match = null, // for lookup, leaving null for falsy test later
        i = 0; // iterator / index
    col.name = parts[0]; // store the name
    col.type = column.colType; // and type
    col.index = column.colIndex; // and index, if you want to use it later
    col.subColumns = []; // make an empty array for any subcolumns that may exist so we can concat later
    if (parts.length > 1) {
        col.subColumns.push({ // subcolumn, push it into the array
            "name": parts[1],
            "index": col.index
        });
    }
    for (i = 0; i < columns.length; i += 1) {
        if (columns[i].name === col.name) { // [parent] column already exists
            match = i; // store the index
            break; // exit the loop
        }
    }
    if (match) { // index found
        columns[i].subColumns = columns[i].subColumns.concat(col.subColumns); // concat the subcolumns
    } else {
        columns.push(col); // did not exist, add it to the array
    }
});

Once that's been done, loop through the columns variable twice to make your two rows (assumes variables for thead , tr , and th elements). 完成此操作后,循环遍历columns变量两次以创建两行(假定theadtrth元素的变量)。

columns.forEach(function(column, index, array) { // loop through once to make "parent" headers
    th = document.createElement('th'); // create th element
    th.colSpan = column.subColumns.length; // set a colSpan as necessary
    if (!column.subColumns.length) { //  if there are no subcolumns, make the header span two rows
        th.rowSpan = 2;
    }
    th.innerText = column.name; // set the header text
    tr.appendChild(th); // add to row
});

columns.forEach(function(column, index, array) { // loop through a second time to get the child headers
    column.subColumns.forEach(function(subColumn, index, array) { // loop through the child headers
        th = document.createElement('th'); // create th
        th.innerText = subColumn.name; // set the header text
        tr.appendChild(th); // add to row
    });
});

Here is is in action: 这是实际的:

 var metadata = [{ "colIndex": 0, "colType": "String", "colName": "PM" }, { "colIndex": 1, "colType": "String", "colName": "PROD" }, { "colIndex": 2, "colType": "Numeric", "colName": "NR/M1" }, { "colIndex": 3, "colType": "Numeric", "colName": "NR/M2" }, { "colIndex": 4, "colType": "Numeric", "colName": "NR/M3" }, { "colIndex": 5, "colType": "Numeric", "colName": "NR/M4" }, { "colIndex": 6, "colType": "Numeric", "colName": "NR/M5" }, { "colIndex": 7, "colType": "Numeric", "colName": "NR ART/M6" }, { "colIndex": 8, "colType": "Numeric", "colName": "NR ART/M1" }, { "colIndex": 9, "colType": "Numeric", "colName": "NR ART/M2" }, { "colIndex": 10, "colType": "Numeric", "colName": "NR ART/M3" }, { "colIndex": 11, "colType": "Numeric", "colName": "NR ART/MX" }], columns = [], thead = document.getElementById('dynamic'), // cache this DOM lookup tr = document.createElement('tr'), th; // placeholder for th elements that will be created metadata.forEach(function(column, index, array) { // parse into a more friendly format for making columns var col = {}, // will be the column parts = column.colName.split('/'), // you're splitting on the column name in your post match = null, // for lookup, leaving null for falsy test later i = 0; // iterator / index col.name = parts[0]; // store the name col.type = column.colType; // and type col.index = column.colIndex; // and index, if you want to use it later col.subColumns = []; // make an empty array for any subcolumns that may exist so we can concat later if (parts.length > 1) { col.subColumns.push({ // subcolumn, push it into the array "name": parts[1], "index": col.index }); } for (i = 0; i < columns.length; i += 1) { if (columns[i].name === col.name) { // [parent] column already exists match = i; // store the index break; // exit the loop } } if (match) { // index found columns[i].subColumns = columns[i].subColumns.concat(col.subColumns); // concat the subcolumns } else { columns.push(col); // did not exist, add it to the array } }); columns.forEach(function(column, index, array) { // loop through once to make "parent" headers th = document.createElement('th'); // create th element th.colSpan = column.subColumns.length; // set a colSpan as necessary if (!column.subColumns.length) { // if there are no subcolumns, make the header span two rows th.rowSpan = 2; } th.innerText = column.name; // set the header text tr.appendChild(th); // add to row }); thead.appendChild(tr); // add row to thead tr = document.createElement('tr'); // reset row columns.forEach(function(column, index, array) { // loop through a second time to get the child headers column.subColumns.forEach(function(subColumn, index, array) { // loop through the child headers th = document.createElement('th'); // create th th.innerText = subColumn.name; // set the header text tr.appendChild(th); // add to row }); }); thead.appendChild(tr); // add row to thead 
 th {border: 1px solid black;} 
 <table> <thead id="dynamic"></thead> </table> 

jQuery, if you're using it, can make this syntax a little more "sugary" (mostly in making the rows and header cells, see below), but the principle will be the same. jQuery,如果您正在使用它,可以使此语法更加“有用”(主要是在制作行和标题单元格,请参见下文),但是原理是相同的。

columns.forEach(function(column, index, array) { // loop through once to make "parent" headers
    th.clone().attr({
        "colSpan": column.subColumns.length,
        "rowSpan": column.subColumns.length === 0 ? 2 : 1
    }).text(column.name).appendTo(tr);
});
tr.appendTo(thead);
tr = $('<tr />');
columns.forEach(function(column, index, array) { // loop through a second time to get the child headers
    column.subColumns.forEach(function(subColumn, index, array) { // loop through the child headers
        th.clone().text(subColumn.name).appendTo(tr);
    });
});
tr.appendTo(thead);

After some hours i find an answer to my problem. 几个小时后,我找到了解决我问题的答案。 In this first part i create the tr and th (th non Unique) 在第一部分中,我创建了tr和th(第一个非唯一的)

var a = [], l = 0;
for (var i = 0; i < metadata.length; i++) {
   var d = {
    "colName": metadata[i].colName.split("/"),
        "colType": metadata[i].colType
   };
   a.push(d);
   if (metadata[i].colType == "Numeric") {
      trl = metadata[i].colName.split("/").length;
   }
}
for (var l = 0; l < trl; l++) {
   var tr = $("<tr/>").appendTo("thead");
}

for (var j = 0; j < a.length; j++) {
   if (a[j].colType == "String") {
     $("<th/>").addClass("string").attr("rowspan", l).html(a[j].colName).appendTo("thead tr:nth(0)");
   } else {
      for (var k = 0; k < a[j].colName.length; k++) {
          $("<th/>").addClass("numeric").html(a[j].colName[k]).appendTo("thead tr:nth(" + k + ")");
      }
  }
}

This part is adapted from here . 这部分从这里改编。 Now i clean up the table thead living just unique th. 现在,我收拾桌子thead居住唯一。

$('table tr').each(function () {
  var tr = this;
  var counter = 0;
  var strLookupText = '';

  $('th.numeric', tr).each(function (index, value) {
    var td = $(this);
    if ((td.text() == strLookupText) || (td.text() == "")) {
        counter++;
        td.prev().attr('colspan', '' + parseInt(counter + 1, 10) + '').css({
            textAlign: 'center'
        });
        td.remove();
    } else {
        counter = 0;
    }
    strLookupText = td.text();
   });
});

The code it's a litle messy, but it's late. 代码有点混乱,但是已经晚了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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