Text Wrapping in AG Grid Column Headers & Cells

  |   How To

Today I’ll show you how to implement one of the most sought-after features with the grid: automatically wrapping text in both column headers and cells. This will let you easily show long text values in your grids as shown below:

Live demo code

See this live example code in all the major frameworks here:
  •  Angular
  •  React
  •  JavaScript
  •  Vue

In this article we'll build a grid whose headers and rows can automatically adjust their heights to accommodate any column header text length, whether that be a single line or four, no ellipses and no cutoffs, if it's there, we'll show it!

In this blog, we'll be going over:

Cell Text Wrapping

Wrapping text in cells is supported by AG Grid making this an easy feature to support via AG Grid's inbuilt auto row height & text wrapping features. Both of these features can be turned on by amending a column's definitions—or defaultColDef—like so:

var defaultColDef = {
    flex: 1,
    resizable: true,
    sortable: true
    wrapText: true,     // <-- HERE
    autoHeight: true,   // <-- & HERE    

With both of these enabled cell text will wrap and the row height automatically change to ensure all the cell text remains visible as seen in the example above.

Header Text Wrapping

In version 28 of AG Grid we added support for wrapping header cell text. This is done with the Wrap Header Text and Auto Header Height properties.

Adding wrapHeaderText: true will cause long headers to wrap, but if some are longer than the actual height of the header then they will be shown truncated. To avoid this we combine the use of wrapHeaderText with autoHeaderHeight: true.

In the example configuration below, you can see that both wrapHeaderText and autoHeaderHeight are set to true and no additional work is required to achieve wrapped text in headers.

const gridOptions = {
  defaultColDef: {
    resizable: true,
    initialWidth: 200,
    wrapHeaderText: true,
    autoHeaderHeight: true,
  },
  columnDefs: columnDefs,
};

Thats it! No custom CSS or Javascript is required as AG Grid handles this for you.

Custom Header Text Wrapping (pre v28)

The rest of this blog post explains the use of header templates. These are a very flexible way of creating custom headers quickly and could be used to simulate this header wrapping functionality if you are working with an earlier version of AG Grid.

Part 1: Setting Up

Let’s quickly run through the simple grid that I will be building this example upon. This Plunker will be our base-case.

As you can see, there are three columns, each has a header with a long text—courtesy of Lorem Ipsum—and a couple of rows just for good measure—because what is a grid without any rows!?

Part 2: Column Header Template

When customising column headers, it can be useful to declare our own column header template which the grid will use instead of the default. This allows us to see how we're interacting with our grid's headers to achieve our goal. In this case, we will copy and use the column header template provided in our documentation:

var defaultColDef = {
    flex: 1,
    resizable: true,
    sortable: true,
    headerComponentParams: {
        template:
            '<div class="ag-cell-label-container" role="presentation">' +
            '  <span ref="eMenu" class="ag-header-icon ag-header-cell-menu-button"></span>' +
            '  <div ref="eLabel" class="ag-header-cell-label" role="presentation">' +
            '    <span ref="eSortOrder" class="ag-header-icon ag-sort-order"></span>' +
            '    <span ref="eSortAsc" class="ag-header-icon ag-sort-ascending-icon"></span>' +
            '    <span ref="eSortDesc" class="ag-header-icon ag-sort-descending-icon"></span>' +
            '    <span ref="eSortNone" class="ag-header-icon ag-sort-none-icon"></span>' +
            '    <span ref="eText" class="ag-header-cell-text" role="columnheader" style="white-space: normal;"></span>' +
            '    <span ref="eFilter" class="ag-header-icon ag-filter-icon"></span>' +
            '  </div>' +
            '</div>',
    },
};
As this template will be used for all of our columns, I’ve declared it in the defaultColDef

Note: The style declared for the eText span: white-space: normal; is crucial for making sure the text will display how we want it. If you're not familiar with this, the I'd recommend reading up on it here.  

Part 3: Getting the Column Header Height

We'll use two functions to get the effect we're after; the first will ask the question: "How tall should the headers be?" And the second will implement the answer from the first. Let’s call the first function headerHeightGetter().

To get the required height, we'll use the element.clientHeight property of the texts inside each column header. If you’re unfamiliar with clientHeight, check out its entry on W3Schools or MDN.

However, before querying my elements on the DOM, I need to put them in an array. This will allow me to later map() over them to find the tallest header to set the height of all column headers. To do this, I’m going to use document.querySelectorAll(), and give it the class declared in the header template: 'ag-header-cell-text'. As querySelectorAll() will return a Nodelist—find out more here—we will be using the spread syntax to create an array which we can then query via dot notation as shown below:

    var columnHeaderTexts = [
        ...document.querySelectorAll('.ag-header-cell-text'),
    ];

Once we’ve got our array, we can map over its elements to get the header heights like this:

    var clientHeights = columnHeaderTexts.map(
        headerText => headerText.clientHeight
    );

Lastly, using Math.max() we can find the largest number in the clientHeights and return it.

function headerHeightGetter() {
    var columnHeaderTexts = [
        ...document.querySelectorAll('.ag-header-cell-text'),
    ];
    var clientHeights = columnHeaderTexts.map(
        headerText => headerText.clientHeight
    );
    var tallestHeaderTextHeight = Math.max(...clientHeights);

    return tallestHeaderTextHeight;
}

Part 4: Setting the Column Header Height

Now we have the height required to fully represent our longest column header text, we need to set it via the grid's API. To do this, let’s first create a function called headerHeightSetter(). In this function we create two variables:

  1. The first variable called padding to give the column header a little more room than what’s required to fit the text, we don’t want the text to be just-about squished in, we want the text to have a little room to breathe. Please set the padding value to fit your needs.
  2. The second variable will come from headerHeightGetter() and will be combined with the padding to give us the finalHeight.

Here's the full code implementing this below:

function headerHeightSetter() {
    var padding = 20;
    var height = headerHeightGetter() + padding;
    gridOptions.api.setHeaderHeight(height);
}

Part 5: Header Text Wrapping

Now we’ve built the engine, time to hook up the drive-shaft and give this baby a spin! As the headerHeightSetter() function already calls headerHeightGetter(), all we need to do is trigger it when the grid renders. This can be done in the gridOptions where I’ve hooked headerHeightSetter() up to the onFirstDataRendered callback. This means that our height will be dynamically set when data is rendered on our grid.

var gridOptions = {
    columnDefs: columnDefs,
    rowData: rowData,
    defaultColDef: defaultColDef,
    onFirstDataRendered: headerHeightSetter,
    onColumnResized: headerHeightSetter,
};

However, we’re still missing a case, what if the grid—or a column—is resized? I’d expect the columns to automatically resize to fill the space. Luckily this can be done by hooking up the headerHeightSetter() to another callback: onColumnResized.

Now, each time the width of the grid or columns is changed, the setter is called and the heights are recalculated! Add the resizable: true property to your defaultColDef and run the grid. The column header height adjusts as the user resizes it, as shown below:

Part 6: How Does Sorting Work?

So now we have a grid that can handle multi-line header texts. as it happens, it can also handle sorting. But it wasn't just as simple as declaring sortable: true in the defaultColDef.

The astute amongst you may have noticed that in the header template there were elements related to sorting:

'    <span ref="eSortAsc" class="ag-header-icon ag-sort-ascending-icon"></span>' +
'    <span ref="eSortDesc" class="ag-header-icon ag-sort-descending-icon"></span>' +
'    <span ref="eSortNone" class="ag-header-icon ag-sort-none-icon"></span>' +

When we provided a custom header template, we told the grid that wanted full control of everything to do with the header, which includes sorting functionality. Without these lines code, our sorting functionality would not work so make sure to include them or your own version of them. Coincidentally, this also means that they are fully customisable to your heart's desires!

Summary

And there we go, that's multi-line column headers and cell texts! I hope this can help you implement the dynamic multi-line header behaviour your users expect.

To find out more about column headers in AG Grid, be sure to check out our documentation which is choc-full of many more features to try out.

Lastly if you're new to AG Grid and want to see what all the hubbub is about, why not try it out — for free — by checking out our getting started guides.

Read more posts about...