jQuery Grids with Multi-Column Headers

Damyan Petev / Tuesday, March 12, 2013

jQuery Grids Multi-Column HeadersIf you’ve been reading the latest blog about the Column Moving feature of the Ignite UI Grid I’ve briefly mentioned Multi-column Headers. Well, just in case you missed it – that’s a fairly recent addition too! Through a very simple (and logical I think) setup of columns and layouts, this feature allows for visual and sometimes functional grouping of column headers under a common parent.

While on its own it might be mostly a visual thing, having other parts/modules of the Ignite UI Grid and Hierarchical Grid being aware of it, it can have some really interesting interactions. These might not be part of the actual widgets code, but they are a by-product of having column headers grouped while using other features. And below I’ll be throwing some more feature names at you for reference in one place.

Lastly if you are in for a quick start with the whole toolset and related concepts you can Get Started Learning Ignite UI Today! I could not resist the awesome contained in there and now, with the shameless plug done with, let’s move on to the basics of the feature.

Quick Start

After all you need to start somewhere and if you are like me and just straight to the API to check out capabilities, you are in for a slight shock. As it stands, the Multi-Column Headers widget doesn’t really offer much. So how do you set it up? Well it all becomes very reasonable when you begin to thing about the widget itself as an on/off switch (which is exactly what it does) and the column definitions are where they always were.. in the column collection! You can simply add one that has a collection on it’s own assigned to its ‘group’ property. Inside you can have more columns with groups and so on – the feature should allow for virtually unlimited nested headers. Then you also have some additional properties like a key and row span. A partial snippet with the ASP.NET MVC Helper (important lines are 5 and 6):

  1. @(Html.Infragistics().Grid<MultiColumnHeaders.Models.Department>()
  2.     .Columns(column =>
  3.     {
  4.         column.For(x => x.DepartmentID).HeaderText("DEP Id");
  5.         column.MultiColumnHeader("Dep").HeaderText("Dep")
  6.             .Group(innerColumn =>
  7.             {
  8.                 innerColumn.For(x => x.Name).HeaderText("Name");
  9.                 innerColumn.For(x => x.GroupName).HeaderText("GroupName");
  10.             });
  11.     })
  12.     //...

And it’s as simple in script(line 9):

  1. $('#grid').igHierarchicalGrid({
  2.     columns: [{
  3.         key: 'DepartmentID',
  4.         dataType: 'number',
  5.         headerText: 'DEP Id'
  6.     }, {
  7.         key: 'Dep',
  8.         headerText: 'Dep',
  9.         group: [{
  10.             key: 'Name',
  11.             dataType: 'string',
  12.             headerText: 'Name'
  13.         }, {
  14.             key: 'GroupName',
  15.             dataType: 'string',
  16.             headerText: 'GroupName'
  17.         }]
  18.     }],
  19.     //..

For the above layouts to work simply add the “MultiColumnHeaders” feature to the respective collection:

  1. //....
  2. .Features(feature => feature.MultiColumnHeaders().Inherit(true))
  3. //  ---- OR -----
  4.  features: [{
  5.             name: 'MultiColumnHeaders'
  6.         }],
  7. //...

You have two options here as usual when it comes to the Hierarchical Grid – you can define the feature in the parent’s collection and allow inheritance or add it to the specific layout. Overall you can check out the igGrid Multi-Column Header topics in our help they already contain a fairly detailed guide. Or you can also refer to the samples and demos section below and learn by example :)

Not so fast!

First off, let’s have a little bit of understanding of what exactly the feature does it modifies the markup in the THEAD of the Grid table. The Multi-Column Headers feature provides out-of-the-box alignment - as you’d expect much of it revolves around figuring out the perfect way to align actual column headers in there – setting “colspan” and row spans, the latter you can tweak (more on that below). The only required setting really is the group collection, really. Well, I guess the header text is also really nice to have, because  you’ll end up with header saying ‘undefined’ otherwise.

Then there’s also the key. Unlike your usual column settings, the key in this case doesn't correspond to a data property. So why would you need it? When you don’t assign a multi-column header a key(which doubles as identifier) you will be stuck with whatever the grid comes up with on its own. That means that the identifiers will be generated (incrementing number id) and these don’t always get assigned as you’d expect. Furthermore, perhaps you’ve seen some methods – particularly with API that manipulate columns like Hiding and Moving – that do accept multi-column identifiers/keys. Lacking a key can sometimes cause odd behavior, so it’s best if you consider it required as well. In addition, you don’t want to be missing on the opportunity for easy header targeting:

The THEAD markup generated for the Multi-Column Headers with levels and id-s

When you define a key the headers will also be assigned id-s (combination of the Ignite UI Grid’s Id and the column’s) that you can use for super fast and easy targeting. This puts those headers in line with the data-bound ones you see with “data-isheadercell” attribute set to true.

The good and evil row spans

The ‘rowspan’ property is a direct reference to the underlying table cell property, however the feature itself figures out the alignment of cells in rows (in order to have them span and align the way you’d expect rows sometimes end up containing both data bound and group headers). So there are some ‘gotcha’-s right here. For example take the case below (some columns are hidden and other squished so they can fit in the shot) where we set the “Record Info” group header from it’s original single row span to two in order to push its child column lower and align them with the rest.

An example of the effect of the rowspan property of the MUlti-Column Headers

Notice how the intention to merely align all the ‘actual’ headers in a neat row resulted in enlarging the entire header table. Because this aligned the last two column headers with the rest in a neat row, they really actually ended up in the same table row in markup. And since those were slightly bigger with more text the row expanded to fit them, causing the stretch. Also if you define row spans for columns that don’t have anything else to take up the space or not enough space to fit the rest you can end up needing to compensate elsewhere to avoid skewed layout. Generally, I’d suggest relying on the grid to do that for you. 

More on the API

As you can see duly noted in the documentation the feature’s own getMultiColumnHeaders should be used in order to get the headers and their groups, as if you get the igGrid widget columns collection using $(“.selector”).igGrid(“option”, “columns”) you will get the ‘actual’ ones only – no group hierarchy.

It’s a similar case with the hierarchical grid, but for different reasons. You can actually pull the column options as usual and they will contain the group headers and their children, however that’s only good if you need to check out the header hierarchy. For the additional info, such as col/row span, identifier or level you should again rely on the getMultiColumnHeaders method.

There’s another method, that oddly enough the API is missing, but the documentation doesn’t and I figured it’s good to mention – it’s called "renderMultiColumnHeader" and it takes the very same column collection you’d provide for grid initialization. With it you can change the Multi-column  headers’ arrangement in runtime or even define columns without any – just keep in mind it costs a grid rebind. It’s fairly simple to use too:

$('#Grid1').igGrid("renderMultiColumnHeader", [{
key: 'DepartmentID',
dataType: 'number',
headerText: 'DEP Id'
}, {
headerText: "Dep",
group: [
//...
]
}]);

There are other setting you’d normally see in the API that are shared for all columns (less so with the ASP.NET MVC helpers) that you might set, but will be generally ignored (templates, formatters and such don’t make much sense there). There are some that would, but still will not apply – e.g. the width property. The width of the multi-column header is determined by it’s group and in order to define width for it you just have to set it up on the child columns.

Styling

The feature applies a single specific class(“ui-iggrid-multiheader-cell”) to the headers you can target with CSS. This allows for specific styles to differentiate the group headers from the rest, which is something that I think the default Infragistics theme needs. You can do anything really, but I decided a subtle underline will do quite nicely (which I totally stole from the Modern UI theme!) so with the following style:

  1. .ui-iggrid th.ui-iggrid-multiheader-cell {
  2.     border-bottom-color: #2cbdf9;
  3.     border-bottom-width: 1px;
  4. }

An example of applying styles targeted at only the Multi-Column Headers

You get something similar to this. You can even use some pseudo-classes to alternate between styles in case you have more groups, which I’ve also done in my samples so check them out below.

Feature interaction

I’ve already mentioned in my last blog Column Moving is playing well with multi headers. And as you can imagine the parent headers can also be dragged in order to move all their children  at once. So what else interesting?

With the Hiding Feature you get a nice icon on the header to use to hide the entire group and parent will go away if all nested columns are hidden. You can also simply set the ‘hidden’ property of the column to hide it by default and that part doesn’t even require the respective feature to work (but it will lack UI and Interactions to show the columns without).

Both features’ API methods that perform column operations will also accept multi-column header identifier in place of the key.

When Resizing the user can interact both with individual columns or with their group header both leading to a satisfactory reactions – resizing nested columns will modify the parent’s width and vice versa.

In terms of UI, both Hiding and Column Moving produce their distinctive header icons for interaction, as seen on the topmost image of the blog. Unlike data-bound columns that accumulate additional features in a single gear icon, the group ones doesn’t support more than the two above and therefore don’t get a feature chooser! That might change as more features that make sense to be bulk-performed are added to the Grids (*hint hint* might happen very soon too).

Resources

Donwload your Ignite UI Free Trial now!

I’d love to hear some thoughts, so leave a comment down below or @DamyanPetev.

And as always, you can follow us on Twitter @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!