By default, exporting a Master Detail grid includes the master rows but not their detail grids. Use getCustomContentBelowRow to return a PDF representation of the detail data that should be inserted below each master row.
Exporting Master And Detail Rows Copy Link
getCustomContentBelowRow receives the master row node and returns PdfCell[][]. Build these rows from the detail data stored on the master record:
const gridOptions = {
defaultPdfExportParams: {
getCustomContentBelowRow: (params) => {
const records = params.node.data.callRecords;
return [
[
{ data: { value: 'Call ID' } },
{ data: { value: 'Direction' } },
{ data: { value: 'Number' } },
{ data: { value: 'Duration / Switch Code' } },
],
...records.map((record) => [
{ data: { value: String(record.callId) } },
{ data: { value: record.direction } },
{ data: { value: record.number } },
{ data: { value: `${record.duration}s / ${record.switchCode}` } },
]),
];
},
},
// other grid options ...
}This approach does not create or export a separate detail-grid instance. It works whether the detail row is expanded or collapsed and avoids creating every detail grid during export.
Detail-grid behaviour is not applied automatically. If detailGridOptions contains Value Getters, Value Formatters, sorting or filtering that should be represented in the PDF, apply the equivalent logic while building the custom rows.
Custom content uses the master table's exported column layout. Build a compact detail representation that fits the number and widths of the exported master columns.
The example below exports a heading, column headers and all call records below each master account row. The duration formatting used by the detail grid is reproduced when the PDF content is created.
import {
ClientSideRowModelModule,
GridApi,
GridOptions,
IDetailCellRendererParams,
ModuleRegistry,
PdfCell,
PdfCellStyle,
ProcessRowGroupForExportParams,
createGrid,
enableDevValidations,
} from "ag-grid-community";
import {
ColumnMenuModule,
ContextMenuModule,
MasterDetailModule,
PdfExportModule,
} from "ag-grid-enterprise";
import { IAccount, ICallRecord } from "./interfaces";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableDevValidations();
}
ModuleRegistry.registerModules([
ClientSideRowModelModule,
MasterDetailModule,
PdfExportModule,
ColumnMenuModule,
ContextMenuModule,
]);
const detailHeadingStyle: PdfCellStyle = {
backgroundColor: "#d9eaf7",
color: "#19486a",
fontWeight: "bold",
padding: 6,
};
const detailHeaderStyle: PdfCellStyle = {
backgroundColor: "#eef4f8",
color: "#243746",
fontWeight: "bold",
padding: 5,
};
function getDetailRows(
params: ProcessRowGroupForExportParams<IAccount>,
): PdfCell[][] {
const account = params.node.data;
if (!account) {
return [];
}
const rows: PdfCell[][] = [
[
{
data: { value: `Calls for ${account.name}` },
mergeAcross: 3,
style: detailHeadingStyle,
},
],
[
{ data: { value: "Call ID" }, style: detailHeaderStyle },
{ data: { value: "Direction" }, style: detailHeaderStyle },
{ data: { value: "Number" }, style: detailHeaderStyle },
{ data: { value: "Duration / Switch Code" }, style: detailHeaderStyle },
],
];
for (const record of account.callRecords) {
rows.push([
{ data: { value: String(record.callId) } },
{ data: { value: record.direction } },
{ data: { value: record.number } },
{ data: { value: `${record.duration}s / ${record.switchCode}` } },
]);
}
return rows;
}
let gridApi: GridApi<IAccount>;
const gridOptions: GridOptions<IAccount> = {
columnDefs: [
{ field: "name", cellRenderer: "agGroupCellRenderer", minWidth: 180 },
{ field: "account" },
{ field: "calls" },
{ field: "minutes", valueFormatter: (params) => `${params.value}m` },
],
defaultColDef: {
flex: 1,
minWidth: 110,
},
masterDetail: true,
detailCellRendererParams: {
detailGridOptions: {
columnDefs: [
{ field: "callId" },
{ field: "direction" },
{ field: "number", minWidth: 150 },
{ field: "duration", valueFormatter: (params) => `${params.value}s` },
{ field: "switchCode", minWidth: 150 },
],
defaultColDef: {
flex: 1,
},
},
getDetailRowData: (params) => {
params.successCallback(params.data.callRecords);
},
} as IDetailCellRendererParams<IAccount, ICallRecord>,
defaultPdfExportParams: {
columnWidth: "auto",
getCustomContentBelowRow: getDetailRows,
},
};
function onBtExport() {
gridApi.exportDataAsPdf();
}
gridApi = createGrid(
document.querySelector<HTMLElement>("#myGrid")!,
gridOptions,
);
fetch("https://www.ag-grid.com/example-assets/master-detail-data.json")
.then((response) => response.json())
.then((data: IAccount[]) => {
gridApi.setGridOption("rowData", data.slice(0, 3));
});
if (typeof window !== "undefined") {
// Attach external event handlers to window so they can be called from index.html
(<any>window).onBtExport = onBtExport;
}
.example-wrapper {
display: flex;
flex-direction: column;
height: 100%;
}
.controls {
margin-bottom: 8px;
}
#myGrid {
flex: 1 1 0px;
width: 100%;
}
<div class="example-wrapper">
<div class="controls">
<button onclick="onBtExport()">Export to PDF</button>
</div>
<div id="myGrid"></div>
</div>
export interface ICallRecord {
name: string;
callId: number;
duration: number;
switchCode: string;
direction: string;
number: string;
}
export interface IAccount {
name: string;
account: number;
calls: number;
minutes: number;
callRecords: ICallRecord[];
} API Copy Link
Export Options Copy Link
See below the functions on the PdfExportParams interface to customise exported grid values.
A callback function to return content to be inserted below a row in the export. |
PdfCell Copy Link
Properties available on the PdfCell interface.
The data that will be added to the cell. |
The number of cells to span across (1 means span 2 columns). |
Optional styling for the cell.
|
PdfCellData Copy Link
Properties available on the PdfCellData interface.
The value of the cell. |
External URI opened when the exported cell text is selected. |
Image rendered alongside the cell value. |
ProcessRowGroupForExportParams Copy Link
Properties available on the ProcessRowGroupForExportParams<TData = any, TContext = any> interface.
Row node. |
The grid column |
The grid api. |
Application context as set on gridOptions.context. |