Most tables and grids include a single header row that shows the name of the field that the column refers to. In many cases, the table data has a hierarchical nature, and it may be desirable to add several levels of column headers to expose the hierarchy. The W3 specification for the table element shows a simple example:
The "height" and "weight" columns are grouped under an "Average" header that makes the table easier to read. You can create this type of table using the FlexGrid (our JavaScript DataGrid), but it requires writing some code. Because this is a fairly common scenario, we wrote some functions that take an object describing the column hierarchy and set everything up automatically. Using these functions, you could create the table in the example above like this:
The "w3Columns" array describes the column hierarchy using a plain JavaScript object. Each element in the array specifies the properties for a column on the grid, and each may have a "columns" property that specifies child columns. You may nest columns to any depth. The "bindColumnGroups" function creates the hierarchical column structure, including the columns themselves and the merged headers. Here is the result: To further illustrate, consider a table that compares the performance and composition of investment funds. You could have a group of columns to show performance and another to show composition:
Notice a few interesting points:
- The column data includes column properties such as “format” and “width”.
- The column data binds columns to sub-properties of the data items (e.g. “perf.ytd”).
And here is the result: To see both examples live and play with the code, please follow this link: http://jsfiddle.net/Wijmo5/gobtdg7t/ The “bindColumnGroups” function is easy to use and to customize. In the next few sections we will walk through the implementation so you will get a good understanding of how it works, what assumptions it makes, and how you can use or customize it to suit your needs. The “bindColumnGroups” function creates the columns, including their merged headers. It is implemented as follows:
The function starts by setting the grid’s autoGenerateColumns property to false. We will create the columns and don’t want the grid to do it for us when we give it a new itemsSource. Next, the function calls the createColumnGroups to create the columns and extra header cells, and the mergeColumnGroups function to merge the cells in the header. Finally, it uses the formatItem event to align the center-align the content of the header cells. The event handler sets the cell’s display style to “table” and places the cell content into a new div with display set to “table-cell”. This is an easy way to center the cell content vertically. The createColumnGroups function is implemented as follows:
The function loops through the elements in the columnGroups parameter. If a group has no child columns, the function add a column and initializes its properties from the group data. If a group does have child columns, the function calls itself recursively to create the child columns, then applies the group caption to the header cells for the whole group. At this point, the columns and their headers are ready, but they are not merged yet. This is the job of the mergeColumnGroups function:
The function starts by setting the grid’s allowMerging property to “ColumnHeaders”. This assumes you don’t want the grid to merge the data cells. Next, the function enables merging on all column header rows and columns by setting their allowMerging property to true. We will not define a custom merge manager, so the grid will automatically merge header cells that have the same content. This assumes that you won’t have columns or groups with the same name (or if you do, at least they won’t be next to each other). The final block of code handles the situation where header cells have empty cells below them (this happens when groups have fewer levels than the maximum. In this case, the code simply copies the content from the cell above so they merge vertically.
FlexGrid is also available as an Angular DataGrid, React DataGrid and Vue DataGrid.