Exporting AG Grid to PDF with pdfMake
In this blog, you will learn how to export AG Grid to a PDF using pdfMake, a document generation library for JavaScript. We'll demonstrate how to export your grid, including user configurations, such as filters, row groups, and pivots, as well as styles such as row & border colours.
Use the sidebar and column headers to configure the grid and then click "Export to PDF" to download the grid as a PDF:
This blog is focused on JavaScript, but we have examples showing how to export AG Grid to PDF in React, Angular and Vue, too. You can view or download the code for the demo in each of the major frameworks here:
Exporting a Simple Grid
It's very simple to export an AG Grid data grid to PDF by using pdfMake's table functionality, which takes our data (e.g. our headers, rows and columns) and creates a table within a PDF document.
This section covers how to extract the headers, rows and columns from your grid and pass them to pdfMake via a PDF Document Definiton Object.
Headers
First, let's define a getHeaderToExport
function to extract the column header names, and any sorting applied by the user, from the grid header:
/**
* This function iterates over all of the columns to create a row of header cells
*/
const getHeaderToExport = (gridApi) => {
const columns = gridApi.getAllDisplayedColumns();
return columns.map((column) => {
const { field } = column.getColDef();
const sort = column.getSort();
const headerNameUppercase =
field[0].toUpperCase() + field.slice(1);
const headerCell = {
text: headerNameUppercase + (sort ? ` (${sort})` : ''),
};
return headerCell;
});
};
In this function, we iterate over the array of columns returned from the getAllDisplayedColumns API, and extract the field and sort properties from the column definition using getColDef and getSort.
We then parse the field
property to define the text for each header cell by capitalising the first letter and including any sorting applied by the user. The result would look something like this:
[{ text: "ColumnA (asc)" }, { text: "ColumnB" }, ...]
Rows
Now we have an array of column header cells, we need a similar function to extract the cells from the grid rows - getRowsToExport
:
/**
* This function iterates over all of the rows and columns to create
* a matrix of cells
*/
const getRowsToExport = (gridApi) => {
const columns = gridApi.getAllDisplayedColumns();
const getCellToExport = (column, node) => ({
text: gridApi.getValue(column, node) ?? '',
});
const rowsToExport = [];
gridApi.forEachNodeAfterFilterAndSort((node) => {
const rowToExport = columns.map((column) =>
getCellToExport(column, node)
);
rowsToExport.push(rowToExport);
});
return rowsToExport;
};
First, we iterate over every row with the forEachNodeAfterFilterAndSort API to extract each row node, whilst taking into account any filters or sorting that may have been applied by the user. Then, as we did in the last step, we loop through each column returned by getAllDisplayedColumns
, this time extracting the cell value using the getValue
API.
PDF Document Definition Object
With our data ready to export, all we need to do this pull this together into a PDF Document Definition Object which defines how our table is created by pdfMake.
To do this, we've defined the getDocument
function, which takes our data and returns the PDF Document Definition Object
/**
* This function returns a PDF document definition object - the input for pdfMake.
*/
const getDocument = (gridApi) => {
const columns = gridApi.getAllDisplayedColumns();
const headerRow = getHeaderToExport(gridApi);
const rows = getRowsToExport(gridApi);
return {
pageOrientation: 'landscape', // can also be 'portrait'
content: [
{
table: {
// the number of header rows
headerRows: 1,
// the width of each column, can be an array of widths
widths: `${100 / columns.length}%`,
// all the rows to display, including the header rows
body: [headerRow, ...rows],
// Header row is 40px, other rows are 15px
heights: (rowIndex) => (rowIndex === 0 ? 40 : 15),
},
},
],
};
};
Inside this object, we're configuring the orientation of the page, the number of header rows, width of the columns, the body of the table (e.g. our headers and rows), and the height of the rows - everything pdfMake needs to create a table.
Creating & Downloading PDF
Finally, it's as simple as defining an export function to call within your code, and passing the PDF Document Definition Object to pdfMake:
export const exportToPDF = (gridApi) => {
const doc = getDocument(gridApi);
pdfMake.createPdf(doc).download();
};
Example pdfMake Document Definition Object
Here's an example of a PDF document definition object which would define a simple grid:
{
pageOrientation: 'landscape', // can also be 'portrait'
content: [
{
table: {
// the number of header rows
headerRows: 1,
// the width of each column, can be an array of widths
widths: `25%`,
// all the rows to display, including the header rows
body: [
// As we have defined headerRows as '1', only the first row
// is used as the header
[
{ text: 'Athlete' },
{ text: 'Country' },
{ text: 'Age' },
{ text: 'Sport' },
],
// All of the following rows are the body rows of your table
[
{ text: 'Natalie Coughlin' },
{ text: 'United States' },
{ text: '25' },
{ text: 'Swimming' },
],
[
{ text: 'Kirsty Coventry' },
{ text: 'Zimbabwe' },
{ text: '24' },
{ text: 'Swimming' },
]
],
// Header row is 40px, other rows are 15px
heights: (rowIndex) => (rowIndex === 0 ? 40 : 15),
},
},
],
}
This would result in a PDF of this grid being generated and exported by pdfMake:
Athlete | Country | Age | Sport |
---|---|---|---|
Natalie Coughlin | United States | 25 | Swimming |
Kirsty Coventry | Zimbabe | 24 | Swimming |
Styling Your Grid
Using the code snippets provided already, AG Grid can be exported to PDF without any styling. The pdfMake library supports a variety of styling and layout options for exported grids which can be applied by adjusting the PDF Document Definition Object.
To adjust the layout of the exported grid, you can define your own table layout.
First, define the variables you will need to assign colours to your rows and the borders of your PDF:
// Row colors
const HEADER_ROW_COLOR = '#f8f8f8';
const EVEN_ROW_COLOR = '#fcfcfc';
const ODD_ROW_COLOR = '#fff';
const PDF_INNER_BORDER_COLOR = '#dde2eb';
const PDF_OUTER_BORDER_COLOR = '#babfc7';
Then, create a table layout passing in those colours, along with any spacing you require in your exported grid:
const createLayout = (numberOfHeaderRows) => ({
fillColor: (rowIndex) => {
if (rowIndex < numberOfHeaderRows) {
return HEADER_ROW_COLOR;
}
return rowIndex % 2 === 0 ? EVEN_ROW_COLOR : ODD_ROW_COLOR;
},
//vLineHeight not used here.
vLineWidth: (rowIndex, node) =>
rowIndex === 0 || rowIndex === node.table.widths.length ? 1 : 0,
hLineColor: (rowIndex, node) =>
rowIndex === 0 || rowIndex === node.table.body.length
? PDF_OUTER_BORDER_COLOR
: PDF_INNER_BORDER_COLOR,
vLineColor: (rowIndex, node) =>
rowIndex === 0 || rowIndex === node.table.widths.length
? PDF_OUTER_BORDER_COLOR
: PDF_INNER_BORDER_COLOR,
});
Then add this function, along with an array to define the PDF pageMargins
, to your getDocument
function:
const getDocument = (gridApi) => {
const columns = gridApi.getAllDisplayedColumns();
const headerRow = getHeaderToExport(gridApi);
const rows = getRowsToExport(gridApi);
return {
pageOrientation: 'landscape', // can also be 'portrait'
content: [
{
table: {
// the number of header rows
headerRows: 1,
// the width of each column, can be an array of widths
widths: `${100 / columns.length}%`,
// all the rows to display, including the header rows
body: [headerRow, ...rows],
// Header row is 40px, other rows are 15px
heights: (rowIndex) => (rowIndex === 0 ? 40 : 15),
},
layout: createLayout(1),
},
],
pageMargins: [10, 10, 10, 10],
};
};
Styling Headers
To differentiate the header cells from the rows, we can add styling within the headerCell
definition in the getHeaderToExport
function.
Replace the previously defined function with this snippet which adds bold
and margin
properties to the headerCell
:
const getHeaderToExport = (gridApi) => {
const columns = gridApi.getAllDisplayedColumns();
return columns.map((column) => {
const { field, sort } = column.getColDef();
const headerNameUppercase =
field[0].toUpperCase() + field.slice(1);
const headerCell = {
text: headerNameUppercase + (sort ? ` (${sort})` : ''),
// styles
bold: true,
margin: [0, 12, 0, 0],
};
return headerCell;
});
};
Applying Row Styles
We can access the row cellStyle property using the AG Grid column reference methods and then pass those styles to the pdfMake library.
To do this, replace the previously defined getRowsToExport
function with this snippet, which adds cell Styles to the rowToExport
object by calling getColDef().cellStyle
:
const getRowsToExport = (gridApi) => {
const columns = gridApi.getAllDisplayedColumns();
const getCellToExport = (column, node) => ({
text: gridApi.getValue(column, node) ?? '',
// styles
...column.getColDef().cellStyle,
});
const rowsToExport = [];
gridApi.forEachNodeAfterFilterAndSort((node) => {
const rowToExport = columns.map((column) => getCellToExport(column, node));
rowsToExport.push(rowToExport);
});
return rowsToExport;
};
Other styles which are supported by pdfMake can be applied to your exported grid similarly.
Exporting Row Groups
Row Groups can be exported by adapting the getHeaderToExport
function.
The only change we need to make here is overriding the field
property with headername
as the 'Group' column is generated by AG Grid so it doesn't have a field
!
/**
* This function iterates over all of the columns to create a row of header cells and supports row grouping
*/
const getHeaderToExport = (gridApi) => {
const columns = gridApi.getAllDisplayedColumns();
return columns.map((column) => {
const { field, sort } = column.getColDef();
// Enables export when row grouping
const headerName = column.getColDef().headerName ?? field;
const headerNameUppercase =
headerName[0].toUpperCase() + headerName.slice(1);
const headerCell = {
text: headerNameUppercase + (sort ? ` (${sort})` : ''),
// styles
bold: true,
margin: [0, 12, 0, 0],
};
return headerCell;
});
};
Exporting a Pivoted Grid
Pivoted grids can also be exported. First, make sure you've followed the steps above for Row Groups! Then, we need to adapt our getRowsToExport
function:
/**
* This function iterates over all of the rows and columns to create
* a matrix of cells when pivoting is enabled
*/
const getRowsToExportPivot = (gridApi) => {
const columns = gridApi.getAllDisplayedColumns();
const getCellToExport = (column, node) => ({
text: gridApi.getValue(column, node) ?? '',
// styles
...column.getColDef().cellStyle,
});
const rowsToExport = [];
gridApi.forEachNodeAfterFilterAndSort((node) => {
if (node.aggData) {
const rowToExport = columns.map((column) =>
getCellToExport(column, node)
);
rowsToExport.push(rowToExport);
}
});
return rowsToExport;
};
The original getRowsToExport
function exports all the rows, including those which are not displayed in pivot mode. Pivot mode only displays rows with aggregated data so we only want to export these. To achieve this, we have added an if statement to check that the node contains aggregated data by calling node.aggData
. Only those that do are then passed to the getCellToExport
function.
What's next?
We hope this article helps you start exporting AG Grid to PDF!
If you need a different method to export your AG Grid, don't forget about the in-built functionality to export AG Grid to Excel and CSV.
If you would like to try out AG Grid check out our getting started guides (JS / React / Angular / Vue)
Happy coding!