You can specify what header renderer to use at the column definition level. If not specified, the grid's default header rendering components will be used. If you just want to make simple layout changes to the default header take a look at Header Templates.
There are two types of header components:
Below is an example of header component:
class CustomHeader {
init(agParams) {
this.agParams = agParams;
this.eGui = document.createElement('div');
this.eGui.innerHTML = `
<div class="customHeaderMenuButton">
<i class="fa ${this.agParams.menuIcon}"></i>
</div>
<div class="customHeaderLabel">${this.agParams.displayName}</div>
<div class="customSortDownLabel inactive">
<i class="fa fa-long-arrow-alt-down"></i>
</div>
<div class="customSortUpLabel inactive">
<i class="fa fa-long-arrow-alt-up"></i>
</div>
<div class="customSortRemoveLabel inactive">
<i class="fa fa-times"></i>
</div>
`;
this.eMenuButton = this.eGui.querySelector(".customHeaderMenuButton");
this.eSortDownButton = this.eGui.querySelector(".customSortDownLabel");
this.eSortUpButton = this.eGui.querySelector(".customSortUpLabel");
this.eSortRemoveButton = this.eGui.querySelector(".customSortRemoveLabel");
if (this.agParams.enableMenu) {
this.onMenuClickListener = this.onMenuClick.bind(this);
this.eMenuButton.addEventListener('click', this.onMenuClickListener);
} else {
this.eGui.removeChild(this.eMenuButton);
}
if (this.agParams.enableSorting) {
this.onSortAscRequestedListener = this.onSortRequested.bind(this, 'asc');
this.eSortDownButton.addEventListener('click', this.onSortAscRequestedListener);
this.onSortDescRequestedListener = this.onSortRequested.bind(this, 'desc');
this.eSortUpButton.addEventListener('click', this.onSortDescRequestedListener);
this.onRemoveSortListener = this.onSortRequested.bind(this, '');
this.eSortRemoveButton.addEventListener('click', this.onRemoveSortListener);
this.onSortChangedListener = this.onSortChanged.bind(this);
this.agParams.column.addEventListener('sortChanged', this.onSortChangedListener);
this.onSortChanged();
} else {
this.eGui.removeChild(this.eSortDownButton);
this.eGui.removeChild(this.eSortUpButton);
this.eGui.removeChild(this.eSortRemoveButton);
}
}
onSortChanged() {
const deactivate = toDeactivateItems => {
toDeactivateItems.forEach(toDeactivate => {
toDeactivate.className = toDeactivate.className.split(' ')[0]
});
}
const activate = toActivate => {
toActivate.className = toActivate.className + " active";
}
if (this.agParams.column.isSortAscending()) {
deactivate([this.eSortUpButton, this.eSortRemoveButton]);
activate(this.eSortDownButton)
} else if (this.agParams.column.isSortDescending()) {
deactivate([this.eSortDownButton, this.eSortRemoveButton]);
activate(this.eSortUpButton)
} else {
deactivate([this.eSortUpButton, this.eSortDownButton]);
activate(this.eSortRemoveButton)
}
}
getGui() {
return this.eGui;
}
onMenuClick() {
this.agParams.showColumnMenu(this.eMenuButton);
}
onSortRequested(order, event) {
this.agParams.setSort(order, event.shiftKey);
}
destroy() {
if (this.onMenuClickListener) {
this.eMenuButton.removeEventListener('click', this.onMenuClickListener)
}
this.eSortDownButton.removeEventListener('click', this.onSortRequestedListener);
this.eSortUpButton.removeEventListener('click', this.onSortRequestedListener);
this.eSortRemoveButton.removeEventListener('click', this.onSortRequestedListener);
this.agParams.column.removeEventListener('sortChanged', this.onSortChangedListener);
}
}
The example below shows a header component in action. The following can be observed in the demo:
suppressMenu=true
, so the header component doesn't show the menu.sortable=false
, so the header component doesn't add sorting logic.Header components work similar to other component types in AG Grid in which they should implement the following interface:
interface IHeaderComp {
// Optional method, gets called once with params
init?(params: IHeaderParams): void;
// Return the DOM element of your component, this is what the grid puts into the DOM
// Can get called more than once
getGui(): HTMLElement;
// Gets called when a new Column Definition has been set for this header.
// If you handle the refresh of your header return true otherwise return false and the grid will re-create your header from scratch.
refresh(params: IHeaderParams): boolean;
// Gets called once by grid when the component is being removed; if your component needs to do any cleanup, do it here
destroy?(): void;
}
The init(params)
method takes a params object with the items listed below. If custom params are provided via the colDef.headerComponentParams
property, these
will be additionally added to the params object, overriding items of the same name if a name clash exists.
Properties available on the IHeaderParams<TData = any, TContext = any>
interface.
columnTypeColumn | The column the header is for. |
display | The name to display for the column. If the column is using a headerValueGetter, the displayName will take this into account.
|
enable | Whether sorting is enabled for the column. Only put sort logic into your header if this is true.
|
enable | Whether menu is enabled for the column. Only display a menu button in your header if this is true.
|
show | Callback to request the grid to show the column menu. Pass in the html element of the column menu to have the grid position the menu over the button.
|
progress | Callback to progress the sort for this column. The grid will decide the next sort direction eg ascending, descending or 'no sort'. Pass multiSort=true if you want to do a multi sort (eg user has Shift held down when they click).
|
set | Callback to set the sort for this column. Pass the sort direction to use ignoring the current sort eg one of 'asc', 'desc' or null (for no sort). Pass multiSort=true if you want to do a multi sort (eg user has Shift held down when they click)
|
templateTypestring | Custom header template if provided to headerComponentParams , otherwise will be undefined . See Header Templates |
e | The header the grid provides. The custom header component is a child of the grid provided header. The grid's header component is what contains the grid managed functionality such as resizing, keyboard navigation etc. This is provided should you want to make changes to this cell, eg add ARIA tags, or add keyboard event listener (as focus goes here when navigating to the header).
|
apiTypeGridApi | The grid api. |
column | The column api. |
contextTypeTContext | Application context as set on gridOptions.context . |
You specify the Header Component, as well Header Group Components, in the column definition (or you can set in the default column definition to impact all columns).
If you're not familiar with registering Custom Components for use within the Grid please refer the the Registering Components documentation first.
In the definitions below we're registering both a column headerComponent
(for the Age
column), as well as a headerGroupComponent
(for the Medals
grouped column).
// a list of column definitions
const colDefs = [{
// these columns use the default header
{headerName: "Athlete", field: "athlete"},
{headerName: "Sport", field: "sport"},
// this column uses a custom header
// component specified in comps
{headerName: "Age", field: "age", headerComponent: MyHeaderComponent},
// you can also specify header components for groups
{
headerName: "Medals",
// custom header component component specified in comps
headerGroupComponent: MyHeaderGroupComponent,
children: [
{headerName: "Gold", field: "gold"},
{headerName: "Silver", field: "silver"},
{headerName: "Gold", field: "bronze"}
]
}
}]
For more information on declaring columns please refer to the Column Definition Docs, and for grouped columns please refer to the Grouped Column Definition Docs.
A Header Component allows customising the inside part of the header. The component is wrapped inside a header cell so that the grid can take care of some complex logic that you should not be worried about, eg the resizing and moving of columns. The HTML of the header cell is similar to the following:
<!-- the ag-header-cell is always provided by AG Grid -->
<!-- column moving and resize logic is put on this element by the grid -->
<div class="ag-header-cell">
<!-- AG Grid will also always provide a resize bar (if column resizing
is enabled) and take care of all the resize logic. the grid usually
floats this element to the right.-->
<div class="ag-header-cell-resize"></div>
<!-- checkbox for selection, if turned on.
the grid usually floats this element to the left. -->
<div class="ag-header-select-all"></div>
<!-- the header component - this is the piece that you can customise -->
<div class="ag-header-component"></div>
The grid is always responsible for the following:
The header component (your bit) will be responsible for the following:
How you interact with the user for sorting (eg do you listen for mouse clicks?) is up to you. The grid helps you by providing column state and events for getting and setting the sort.
After the user requests a sort, you should call ONE of the following:
params.progressSort(multiSort):
This is the simplest. Call it to progress the sort on the column to the next stage. Using this uses the grid logic for working out what the next sort stage is (eg 'descending' normally follows 'ascending').params.setSort(direction, multiSort):
Use this to set to sort to a specific state. Use this if you don't want to use the grids logic for working out the next sort state.// option 1) tell the grid when you want to progress the sorting
myHeaderElement.addEventListener('click', function(event) {
// in this example, we do multi sort if Shift key is pressed
params.progressSort(event.shiftKey);
});
// or option 2) tell the grid when you want to set the sort explicitly
// button that always sorts ASCENDING
mySortAscButton.addEventListener('click', function(event) {
params.setSort('asc', event.shiftKey);
});
// button that always sorts DESCENDING
mySortDescButton.addEventListener('click', function(event) {
params.setSort('desc', event.shiftKey);
});
To know when a column's sort state has change (eg when to update your icons), you should listen for sortChanged
event on the column.
// listen to the column for sort events
column.addEventListener('sortChanged', function() {
// get sort state from column
var sort = column.getSort();
console.log('sort state of column is ' + sort); // prints one of ['asc',desc',null]
// then do what you need, eg set relevant icons visible
var sortingAscending = sort==='asc';
var sortingDescending = sort==='desc';
var notSorting = !sortingAscending && !sortingDescending;
// how you update your GUI accordingly is up to you
});
// don't forget to remove your listener in your destroy code
The header doesn't normally initiate filtering. If it does, use the standard grid API to set the filter. The header will typically display icons when the filter is applied. To know when to show a filter icon, listen to the column for filterChanged events.
// listen to the column for filter events
column.addEventListener('filterChanged', function() {
// when filter changes on the col, this will print one of [true,false]
console.log('filter of column is ' + column.isFilterActive());
});
// don't forget to remove your listener in your destroy code
How you get the user to ask for the column menu is up to you. When you want to display the menu, call the params.showColumnMenu()
callback. The callback takes the HTML element for the button so that it can place the menu over the button (so the menu appears to drop down from the button).
myMenuButton.addEventListener('click', function() {
params.showColumnMenu(myMenuButton);
});
The refresh(params)
method gets called when the application updates the Column Definitions. For example the application could set a headerName
attribute and then set the Column Definitions again. In this instance, the Header Component should update the displayed header name.
It is the responsibility of the Header Component to inspect the Column Definition for relevant changes and updated if needed. If the refresh was successful then true
should be returned. If the refresh was not successful then false
should be returned. If false
is returned, then the grid will destroy and recreate the component. This pattern is consistent with the refresh
method of Cell Renderers.
On top of the parameters provided by the grid, you can also provide your own parameters. This is useful if you want to 'configure' your header component. For example, you might have a header component for formatting currency but that needs the currency symbol.
colDef = {
...
headerComponent: MyHeaderComponent;
headerComponentParams : {
currencySymbol: '£' // the pound symbol will be placed into params
}
}
As with normal headers, AG Grid will always handle resize and column moving. The grid does not handle selection checkbox as this feature is only at the non-grouped header level. The header group component (your bit) is responsible for the following:
The header group component interface is almost identical to the above header component. The only difference is the params
object passed to the init
method.
interface IHeaderGroupComp {
// optional method, gets called once with params
init?(params: IHeaderGroupParams): void;
// can be called more than once, you should return the HTML element
getGui(): HTMLElement;
// optional method, gets called once, when component is destroyed
destroy?(): void;
}
The params passed to init(params)
are as follows:
Properties available on the IHeaderGroupParams<TData = any, TContext = any>
interface.
column | The column group the header is for. |
display | The text label to render. If the column is using a headerValueGetter, the displayName will take this into account.
|
set | Opens / closes the column group |
apiTypeGridApi | The grid api. |
column | The column api. |
contextTypeTContext | Application context as set on gridOptions.context . |
Not all column groups can open and close, so you should display open / close features accordingly. To check if a column group should have open / close functionality, check the isExpandable()
method on the column group.
const showExpandableIcons = params.columnGroup.isExpandable()
To check if a column group is open or closed, check the isExpanded()
method on the column group.
const groupIsOpen = params.columnGroup.isExpanded();
To open / close a column group, use the params.setExpanded(boolean)
method.
// this code toggles the expanded state
const oldValue = params.columnGroup.isExpanded();
const newValue = !oldValue;
params.setExpanded(newValue);
To know if a group is expanded or collapsed, listen for the expandedChanged
event on the column group.
// get a reference to the original column group
const columnGroup = params.columnGroup.getProvidedColumnGroup();
// create listener
const listener = () => { console.log('group was opened or closed'); };
// add listener
columnGroup.addEventListener('expandedChanged', listener);
// don't forget to remove the listener in your destroy method
columnGroup.removeEventListener('expandedChanged', listener);
When using custom header renderers, the custom header renderer is responsible for implementing support for keyboard navigation among its focusable elements. This is why by default, focusing a grid header with a custom header renderer will focus the entire cell instead of any of the elements inside the custom header renderer.
Adding support for keyboard navigation and focus requires a custom suppressHeaderKeyboardEvent
function in grid options. See Suppress Keyboard Events.
An example of this is shown below, enabling keyboard navigation through the custom header elements when pressing Tab and Shift+Tab:
Athlete
header, press the Tab key and notice that the button, textbox and link in the Country
header can be tabbed into. At the end of the cell elements, the tab focus moves to the next Age
header cellThe suppressHeaderKeyboardEvent
callback is used to capture tab events and determine if the user is tabbing forward or backwards. It also suppresses the default behaviour of moving to the next cell if tabbing within the child elements.
If the focus is at the beginning or the end of the cell children and moving out of the cell, the keyboard event is not suppressed, so focus can move between the children elements. Also, when moving backwards, the focus needs to be manually set while preventing the default behaviour of the keyboard press event.