Under normal operation, AG Grid will render each row as a horizontal list of cells. Each cell in the row will correspond to one column definition. It is possible to switch this off and allow you to provide one component to span the entire width of the grid and not use columns. This is useful if you want to embed a complex component inside the grid instead of rendering a list of cells. This technique can be used for displaying panels of information.
See Master / Detail to include full width rows as a child of another row.
Example of Full Width Rows Copy Link
Below shows an example using full width. The following can be noted:
The rows for countries France, Italy and Peru have full width components instead of cells.
Sorting and filtering all work as if the data was displayed as normal.
import {
ClientSideRowModelModule,
GridApi,
GridOptions,
ICellRendererComp,
ICellRendererParams,
IsFullWidthRowParams,
ModuleRegistry,
RowHeightParams,
TextFilterModule,
createGrid,
enableDevValidations,
} from "ag-grid-community";
import { getData } from "./data";
import { FullWidthCellRenderer } from "./fullWidthCellRenderer";
// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
ModuleRegistry.registerModules([TextFilterModule, ClientSideRowModelModule]);
class CountryCellRenderer implements ICellRendererComp {
eGui!: HTMLElement;
init(params: ICellRendererParams) {
const flag = `<img border="0" width="15" height="10" src="https://www.ag-grid.com/example-assets/flags/${params.data.code}.png">`;
const eTemp = document.createElement("div");
eTemp.innerHTML = `<span style="cursor: default;">${flag} ${params.value}</span>`;
this.eGui = eTemp.firstElementChild as HTMLElement;
}
getGui() {
return this.eGui;
}
refresh(params: ICellRendererParams): boolean {
return false;
}
}
let gridApi: GridApi;
const gridOptions: GridOptions = {
columnDefs: [
{ field: "name", cellRenderer: CountryCellRenderer },
{ field: "continent" },
{ field: "language" },
],
defaultColDef: {
flex: 1,
filter: true,
},
rowData: getData(),
getRowHeight: (params: RowHeightParams) => {
// return 100px height for full width rows
if (isFullWidth(params.data)) {
return 100;
}
},
isFullWidthRow: (params: IsFullWidthRowParams) => {
return isFullWidth(params.rowNode.data);
},
// see AG Grid docs cellRenderer for details on how to build cellRenderers
fullWidthCellRenderer: FullWidthCellRenderer,
};
function isFullWidth(data: any) {
// return true when country is Peru, France or Italy
return ["Peru", "France", "Italy"].indexOf(data.name) >= 0;
}
const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);
.full-width-panel {
/* undo the white-space setting Fresh puts in */
white-space: normal;
height: 100%;
width: 100%;
border: 2px solid grey;
border-style: ridge;
box-sizing: border-box;
padding: 5px;
background-color: #99999944;
}
.full-width-flag {
float: left;
padding: 6px;
}
.full-width-summary {
float: left;
margin-right: 10px;
}
.full-width-panel label {
padding-top: 3px;
display: inline-block;
font-size: 12px;
}
.full-width-center {
overflow-y: scroll;
border: 1px solid grey;
padding: 2px;
height: 100%;
box-sizing: border-box;
font-family: cursive;
background-color: #99999944;
}
.full-width-center p {
margin-top: 0px;
}
.full-width-title {
font-size: 20px;
}
export function getData(): any[] {
const rowData = [
{
// these attributes appear in the top level rows of the grid
name: 'Ireland',
continent: 'Europe',
language: 'English',
code: 'ie',
// these are used in the panel
population: 4000000,
},
// and then repeat for all the other countries
{
name: 'Spain',
continent: 'Europe',
language: 'Spanish',
code: 'es',
population: 4000000,
},
{
name: 'United Kingdom',
continent: 'Europe',
language: 'English',
code: 'gb',
population: 4000000,
},
{
name: 'France',
continent: 'Europe',
language: 'French',
code: 'fr',
population: 4000000,
},
{
name: 'Germany',
continent: 'Europe',
language: 'German',
code: 'de',
population: 4000000,
},
{
name: 'Sweden',
continent: 'Europe',
language: 'Swedish',
code: 'se',
population: 4000000,
},
{
name: 'Norway',
continent: 'Europe',
language: 'Norwegian',
code: 'no',
population: 4000000,
},
{
name: 'Italy',
continent: 'Europe',
language: 'Italian',
code: 'it',
population: 4000000,
},
{
name: 'Greece',
continent: 'Europe',
language: 'Greek',
code: 'gr',
population: 4000000,
},
{
name: 'Iceland',
continent: 'Europe',
language: 'Icelandic',
code: 'is',
population: 4000000,
},
{
name: 'Portugal',
continent: 'Europe',
language: 'Portuguese',
code: 'pt',
population: 4000000,
},
{
name: 'Malta',
continent: 'Europe',
language: 'Maltese',
code: 'mt',
population: 4000000,
},
{
name: 'Brazil',
continent: 'South America',
language: 'Portuguese',
code: 'br',
population: 4000000,
},
{
name: 'Argentina',
continent: 'South America',
language: 'Spanish',
code: 'ar',
population: 4000000,
},
{
name: 'Colombia',
continent: 'South America',
language: 'Spanish',
code: 'co',
population: 4000000,
},
{
name: 'Peru',
continent: 'South America',
language: 'Spanish',
code: 'pe',
population: 4000000,
},
{
name: 'Venezuela',
continent: 'South America',
language: 'Spanish',
code: 've',
population: 4000000,
},
{
name: 'Uruguay',
continent: 'South America',
language: 'Spanish',
code: 'uy',
population: 4000000,
},
];
return rowData;
}
import type { ICellRendererComp, ICellRendererParams } from 'ag-grid-community';
export class FullWidthCellRenderer implements ICellRendererComp {
eGui!: HTMLElement;
init(params: ICellRendererParams) {
// trick to convert string of html into dom object
const eTemp = document.createElement('div');
eTemp.innerHTML = this.getTemplate(params);
this.eGui = eTemp.firstElementChild as HTMLElement;
}
getTemplate(params: ICellRendererParams) {
// the flower row shares the same data as the parent row
const data = params.node.data;
const template = `<div class="full-width-panel">
<div class="full-width-flag">
<img border="0" src="https://www.ag-grid.com/example-assets/large-flags/${data.code}.png">
</div>
<div class="full-width-summary">
<span class="full-width-title">${data.name}</span><br/>
<label><b>Population:</b> ${data.population}</label><br/>
<label><b>Language:</b> ${data.language}</label><br/>
</div>
<div class="full-width-center">${this.latinText()}
</div>
</div>`;
return template;
}
latinText() {
return '<p>Sample Text in a Paragraph</p><p>Lorem ipsum dolor sit amet, his mazim necessitatibus te, mea volutpat intellegebat at. Ea nec perpetua liberavisse, et modo rebum persius pri. Velit recteque reprimique quo at. Vis ex persius oporteat, esse voluptatum moderatius te vis. Ex agam suscipit aliquando eum. Mediocrem molestiae id pri, ei cibo facilisis mel. Ne sale nonumy sea. Et vel lorem omittam vulputate. Ne prima impedit percipitur vis, erat summo an pro. Id urbanitas deterruisset cum, at legere oportere has. No saperet lobortis elaboraret qui, alii zril at vix, nulla soluta ornatus per ad. Feugiat consequuntur vis ad, te sit quodsi persequeris, labore perpetua mei ad. Ex sea affert ullamcorper disputationi, sit nisl elit elaboraret te, quodsi doctus verear ut eam. Eu vel malis nominati, per ex melius delenit incorrupte. Partem complectitur sed in. Vix dicta tincidunt ea. Id nec urbanitas voluptaria, pri no nostro disputationi. Falli graeco salutatus pri ea.</p><p>Quo ad omnesque phaedrum principes, tale urbanitas constituam et ius, pericula consequat ad est. Ius tractatos referrentur deterruisset an, odio consequuntur sed ad. Ea molestie adipiscing adversarium eos, tale veniam sea no. Mutat nullam philosophia sed ad. Pri eu dicta consulatu, te mollis quaerendum sea. Ei doming commodo euismod vis. Cu modus aliquip inermis his, eos et eirmod regione delicata, at odio definiebas vis.</p><p>Lorem ipsum dolor sit amet, his mazim necessitatibus te, mea volutpat intellegebat at. Ea nec perpetua liberavisse, et modo rebum persius pri. Velit recteque reprimique quo at. Vis ex persius oporteat, esse voluptatum moderatius te vis. Ex agam suscipit aliquando eum. Mediocrem molestiae id pri, ei cibo facilisis mel. Ne sale nonumy sea. Et vel lorem omittam vulputate. Ne prima impedit percipitur vis, erat summo an pro. Id urbanitas deterruisset cum, at legere oportere has. No saperet lobortis elaboraret qui, alii zril at vix, nulla soluta ornatus per ad. Feugiat consequuntur vis ad, te sit quodsi persequeris, labore perpetua mei ad. Ex sea affert ullamcorper disputationi, sit nisl elit elaboraret te, quodsi doctus verear ut eam. Eu vel malis nominati, per ex melius delenit incorrupte. Partem complectitur sed in. Vix dicta tincidunt ea. Id nec urbanitas voluptaria, pri no nostro disputationi. Falli graeco salutatus pri ea.</p><p>Quo ad omnesque phaedrum principes, tale urbanitas constituam et ius, pericula consequat ad est. Ius tractatos referrentur deterruisset an, odio consequuntur sed ad. Ea molestie adipiscing adversarium eos, tale veniam sea no. Mutat nullam philosophia sed ad. Pri eu dicta consulatu, te mollis quaerendum sea. Ei doming commodo euismod vis. Cu modus aliquip inermis his, eos et eirmod regione delicata, at odio definiebas vis.</p>';
}
getGui() {
return this.eGui;
}
refresh(params: ICellRendererParams): boolean {
return false;
}
}
<div id="myGrid" style="height: 100%"></div>
Understanding Full Width Copy Link
A fullWidth (full width) component takes up the entire width of the grid. A full width component:
- is not impacted by horizontal scrolling.
- is the width of the grid, regardless of what columns are present.
- is not impacted by pinned sections of the grid, will span left and right pinned areas regardless.
- does not participate in the navigation, Cell Selection (AG Grid Enterprise) or Context Menu (AG Grid Enterprise) of the main grid.
To use fullWidth, you must:
- Implement the
isFullWidthRow(params)callback, to tell the grid which rows should be treated asfullWidth. - Provide a
fullWidthCellRenderer, to tell the grid whatcellRendererto use when doingfullWidthrendering.
Provide your own cell renderer component to use for full width rows. |
The cell renderer can be any AG Grid cell renderer. Refer to Cell Rendering on how to build cell renderers. The cell renderer for fullWidth has one difference to normal cell renderers: the parameters passed are missing the value and column information as the cell renderer is not tied to a particular column. Instead you should use the data parameter, which represents the value for the entire row.
The isFullWidthRow(params) callback receives a params object containing the rowNode as its input and should return true to use fullWidth or false to render as normal.
Sorting and Filtering Copy Link
Sorting and Filtering are NOT impacted by full width; full width is a rendering time feature. The sorting and filtering applied to the data is done before rendering and is not impacted.
Detailed Full Width Example Copy Link
The example below demonstrates full width with pinned rows and columns. The example's data is minimalistic to focus on how full width impacts rows. For demonstration, the pinned rows are shaded blue (with full width a darker shade of blue) and unpinned full width rows are green.
The following points should be noted:
Full width can be applied to any row, including pinned rows. The example demonstrates full width in pinned top, pinned bottom and body rows.
Full width rows can be of any height, which is specified in the usual way using the
getRowHeight(params)callback. The example sets bodyfullWidthrows to 75px.The pinned full width rows are not impacted by either vertical or horizontal scrolling.
The unpinned full width rows are impacted by vertical scrolling only, and not horizontal scrolling.
The full width rows span the entire grid, including the pinned left and pinned right sections.
The full width rows are the width of the grid, despite the grid requiring horizontal scrolling to show the cells.
The example is showing a flat list of data. There is no grouping or parent / child relationships between the full width and normal rows.
The buttons log to the developer console.
import {
ClientSideRowModelModule,
ColDef,
ColumnApiModule,
GridApi,
GridOptions,
IsFullWidthRowParams,
ModuleRegistry,
PinnedRowModule,
RowHeightParams,
createGrid,
enableDevValidations,
} from "ag-grid-community";
import { FullWidthCellRenderer } from "./fullWidthCellRenderer";
// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
ModuleRegistry.registerModules([
ColumnApiModule,
PinnedRowModule,
ClientSideRowModelModule,
]);
const rowData = createData(100, "body");
function getColumnDefs() {
const columnDefs: ColDef[] = [];
alphabet().forEach((letter) => {
const colDef: ColDef = {
headerName: letter,
field: letter,
width: 150,
};
if (letter === "A") {
colDef.pinned = "left";
}
if (letter === "Z") {
colDef.pinned = "right";
}
columnDefs.push(colDef);
});
return columnDefs;
}
let gridApi: GridApi;
const gridOptions: GridOptions = {
columnDefs: getColumnDefs(),
rowData,
enableRowPinning: true,
isRowPinned: (node) => {
if ([51, 52, 53].includes(node.rowIndex!)) {
return "top";
}
if ([96, 97, 98].includes(node.rowIndex!)) {
return "bottom";
}
return null;
},
isFullWidthRow: (params: IsFullWidthRowParams) => {
// in this example, we check the fullWidth attribute that we set
// while creating the data. what check you do to decide if you
// want a row full width is up to you, as long as you return a boolean
// for this method.
return params.rowNode.data.fullWidth;
},
// see AG Grid docs cellRenderer for details on how to build cellRenderers
// this is a simple function cellRenderer, returns plain HTML, not a component
fullWidthCellRenderer: FullWidthCellRenderer,
getRowHeight: (params: RowHeightParams) => {
// you can have normal rows and full width rows any height that you want
const isBodyRow = params.node.rowPinned === undefined;
const isFullWidth = params.node.data.fullWidth;
if (isBodyRow && isFullWidth) {
return 75;
}
},
};
function alphabet() {
return "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
}
function createData(count: number, prefix: string) {
const rowData = [];
for (let i = 0; i < count; i++) {
const item: any = {};
// mark every third row as full width. how you mark the row is up to you,
// in this example the example code (not the grid code) looks at the
// fullWidth attribute in the isFullWidthRow() callback. how you determine
// if a row is full width or not is totally up to you.
item.fullWidth = i % 3 === 2;
// put in a column for each letter of the alphabet
alphabet().forEach((letter) => {
item[letter] = prefix + " (" + letter + "," + i + ")";
});
rowData.push(item);
}
return rowData;
}
const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);
.example-full-width-pinned-row {
background-color: #2244cc44;
border: 2px solid rgb(32, 32, 171);
/* we want the border to be kept within the row height */
box-sizing: border-box;
/* get the row to fill the available height */
height: 100%;
/* grid sets white-space to one line, need to reset for wrapping the text */
white-space: normal;
}
.example-full-width-row {
background-color: #33cc3344;
border: 2px solid #35af35;
/* we want the border to be kept within the row height */
box-sizing: border-box;
/* get the row to fill the available height */
height: 100%;
/* grid sets white-space to one line, need to reset for wrapping the text */
white-space: normal;
}
import type { ICellRendererComp, ICellRendererParams } from 'ag-grid-community';
export class FullWidthCellRenderer implements ICellRendererComp {
eGui!: HTMLDivElement;
private cssClass!: string;
private message!: string;
init(params: ICellRendererParams) {
// pinned rows will have node.rowPinned set to either 'top' or 'bottom' - see docs for row pinning
if (params.node.rowPinned) {
this.cssClass = 'example-full-width-pinned-row';
this.message = `Pinned full width row at index ${params.node.rowIndex}`;
} else {
this.cssClass = 'example-full-width-row';
this.message = `Normal full width row at index ${params.node.rowIndex}`;
}
this.eGui = document.createElement('div');
this.eGui.innerHTML = `<div class="${this.cssClass}"><button>Click</button> ${this.message}</div>`;
const eButton = this.eGui.querySelector('button')!;
eButton.addEventListener('click', function () {
console.log('button clicked');
});
}
getGui() {
return this.eGui.firstChild as any;
}
refresh() {
return false;
}
}
<div id="myGrid" style="height: 100%"></div>
Embedded Full Width Rows Copy Link
By default, Full Width Rows remain in place while the grid is scrolled horizontally. However, this may be undesirable for some applications which need to horizontally scroll the full-width rows together with the rest of the rows.
In order to have Full Width Rows scroll like normal rows, set embedFullWidthRows=true in the gridOptions.
The example below demonstrates the behaviour when Full Width Rows are embedded in the same container as regular rows. Note the following:
- A different instance of the Full Width Cell Renderer is created for each one of the following sections: Pinned Left, Pinned Right, Non Pinned.
- Full Width Rows in the non pinned section take the whole width of the section and scroll horizontally.
- Full Width Rows in the pinned sections take the whole width of the section.
- The renderer can hide a pinned section by returning
nullfromgetGui(). When a pinned section is hidden, the non pinned section expands to fill the available space. - In the example below, the left pinned section is hidden for every 4th full-width row, and the right pinned section is hidden for every 2nd full-width row.
- The buttons log to the developer console.
import {
ClientSideRowModelModule,
ColDef,
ColumnApiModule,
GridApi,
GridOptions,
IsFullWidthRowParams,
ModuleRegistry,
RowHeightParams,
createGrid,
enableDevValidations,
} from "ag-grid-community";
import { FullWidthCellRenderer } from "./fullWidthCellRenderer";
// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
enableDevValidations();
}
ModuleRegistry.registerModules([ColumnApiModule, ClientSideRowModelModule]);
const rowData = createData(100, "body");
function getColumnDefs() {
const columnDefs: ColDef[] = [];
alphabet().forEach((letter) => {
const colDef: ColDef = {
headerName: letter,
field: letter,
width: 100,
};
if (letter === "A" || letter === "B") {
colDef.pinned = "left";
}
if (letter === "Z" || letter === "Y") {
colDef.pinned = "right";
}
columnDefs.push(colDef);
});
return columnDefs;
}
let gridApi: GridApi;
const gridOptions: GridOptions = {
columnDefs: getColumnDefs(),
rowData: rowData,
embedFullWidthRows: true,
isFullWidthRow: (params: IsFullWidthRowParams) => {
// in this example, we check the fullWidth attribute that we set
// while creating the data. what check you do to decide if you
// want a row full width is up to you, as long as you return a boolean
// for this method.
return params.rowNode.data.fullWidth;
},
// see AG Grid docs cellRenderer for details on how to build cellRenderers
// this is a simple function cellRenderer, returns plain HTML, not a component
fullWidthCellRenderer: FullWidthCellRenderer,
getRowHeight: (params: RowHeightParams) => {
// you can have normal rows and full width rows any height that you want
const isBodyRow = params.node.rowPinned === undefined;
const isFullWidth = params.node.data.fullWidth;
if (isBodyRow && isFullWidth) {
return 75;
}
},
};
function alphabet() {
return "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
}
function createData(count: number, prefix: string) {
const rowData = [];
for (let i = 0; i < count; i++) {
const item: any = {};
// mark every third row as full width. how you mark the row is up to you,
// in this example the example code (not the grid code) looks at the
// fullWidth attribute in the isFullWidthRow() callback. how you determine
// if a row is full width or not is totally up to you.
item.fullWidth = i % 3 === 2;
// put in a column for each letter of the alphabet
alphabet().forEach((letter) => {
item[letter] = prefix + " (" + letter + "," + i + ")";
});
rowData.push(item);
}
return rowData;
}
const gridDiv = document.querySelector<HTMLElement>("#myGrid")!;
gridApi = createGrid(gridDiv, gridOptions);
.example-full-width-pinned {
background-color: #2244cc44;
border: 2px solid rgb(32, 32, 171);
/* we want the border to be kept within the row height */
box-sizing: border-box;
/* get the row to fill the available height */
height: 100%;
/* grid sets white-space to one line, need to reset for wrapping the text */
white-space: normal;
}
.example-full-width-row {
background-color: #33cc3344;
border: 2px solid #35af35;
/* we want the border to be kept within the row height */
box-sizing: border-box;
/* get the row to fill the available height */
height: 100%;
/* grid sets white-space to one line, need to reset for wrapping the text */
white-space: normal;
}
import type { ICellRendererComp, ICellRendererParams } from 'ag-grid-community';
export class FullWidthCellRenderer implements ICellRendererComp {
eGui: HTMLDivElement | undefined;
private cssClass!: string;
private message!: string;
init(params: ICellRendererParams) {
const {
pinned,
node: { rowIndex },
} = params;
// pinned rows will have node.rowPinned set to either 'top' or 'bottom' - see docs for row pinning
if (pinned) {
if ((pinned === 'left' && rowIndex! % 4 === 0) || (pinned === 'right' && rowIndex! % 2 === 0)) {
return;
}
this.cssClass = 'example-full-width-pinned';
this.message = `Pinned full width on ${params.pinned} - index ${params.node.rowIndex}`;
} else {
this.cssClass = 'example-full-width-row';
this.message = `Non pinned full width row at index ${params.node.rowIndex}`;
}
this.eGui = document.createElement('div');
this.eGui.innerHTML = `<div class="${this.cssClass}"><button>Click</button> ${this.message}</div>`;
const eButton = this.eGui.querySelector('button')!;
eButton.addEventListener('click', function () {
console.log('button clicked');
});
}
getGui() {
const { eGui } = this;
return !eGui ? null : (eGui.firstChild as any);
}
refresh() {
return false;
}
}
<div id="myGrid" style="height: 100%"></div>
Full Width Keyboard Navigation Copy Link
When using full width rows, the full width cell renderer is responsible for implementing support for keyboard navigation among its focusable elements. This is why by default, focusing a grid cell with a full width cell renderer will focus the entire cell instead of any of the elements inside the full width cell renderer.
Adding support for keyboard navigation and focus requires a custom suppressKeyboardEvent function in grid options. See Suppress Keyboard Events.
An example of this is shown below, enabling keyboard navigation through the full width cell elements when pressing ⇥ Tab and ⇧ Shift+⇥ Tab:
- Click on the
United Kingdomrow, press the ⇥ Tab a few times and notice that the full widthFrancerow can be tabbed into, along with the button, link and textbox. At the end of the cell elements, the tab focus moves to the next cell in the next row - Use ⇧ Shift+⇥ Tab to navigate in the reverse direction
The suppressKeyboardEvent 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.