Excel & PDF Export
The export entries write the grid as it looks: banded headers become merged cells, left-pinned columns and the header rows freeze, groups become collapsible Excel outline levels with footer and total rows (as values or SUBTOTAL formulas), numbers and dates stay typed with Excel formats, and one cellStyle hook carries conditional formatting into both files. Both entries are lazy, so exceljs and jspdf stay out of the main bundle.
export-excel export-pdf group outline SUBTOTAL formulas freeze panes cellStyle
grouped band pinned selection formulas
Select a few rows (Ctrl/Shift+click) to try the selected-rows export; salaries above €8,000 export highlighted.
import { ChangeDetectionStrategy, Component, viewChild } from '@angular/core';
import { OgeColumn, OgeColumnGroup, OgeGrid } from '@oge-ui/grid';
import type { OgeExportCellStyleArgs } from '@oge-ui/grid';
interface Employee {
id: number;
firstName: string;
lastName: string;
department: string;
city: string;
salary: number;
hireDate: string;
}
@Component({
selector: 'demo-root',
imports: [OgeColumn, OgeColumnGroup, OgeGrid],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<oge-grid #grid [data]="employees" keyField="id"
selectionMode="multiple" [groupBy]="['department']">
<oge-column field="id" caption="Id" [width]="70" dataType="number" pinned="left" />
<!-- a band becomes a merged two-row header in Excel and PDF -->
<oge-column-group caption="Employee">
<oge-column field="firstName" caption="First Name" [width]="140" />
<oge-column field="lastName" caption="Last Name" [width]="140" />
</oge-column-group>
<oge-column field="city" caption="City" />
<oge-column field="hireDate" caption="Hired" dataType="date" />
<oge-column field="salary" caption="Salary" dataType="number"
groupSummary="sum" groupSummaryPosition="footer" totalSummary="sum" />
</oge-grid>
<button type="button" (click)="excel()">Excel</button>
<button type="button" (click)="excel(true)">Selected rows</button>
`,
})
export class Demo {
readonly employees = [
{ id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' },
{ id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' },
{ id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' },
{ id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' },
{ id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' },
];
private readonly grid = viewChild.required<OgeGrid<Employee>>('grid');
// conditional formatting: one value-level hook styles Excel and PDF alike
protected readonly cellStyle = ({ column, value, kind }: OgeExportCellStyleArgs<Employee>) =>
kind === 'data' && column.field === 'salary' && Number(value) > 8000
? { background: '#fff4ce', color: '#7a4b00', bold: true }
: undefined;
// the lazy secondary entry keeps exceljs out of the main bundle
protected async excel(selectedRowsOnly = false): Promise<void> {
const { exportGridToExcel } = await import('@oge-ui/grid/export-excel');
await exportGridToExcel(this.grid(), {
filename: 'employees.xlsx',
selectedRowsOnly,
summaryFormulas: true, // group footers + total as SUBTOTAL formulas
columnFormats: { salary: '#,##0 "€"' },
cellStyle: this.cellStyle,
});
}
}PDF page chrome
The PDF repeats the header block on every page, fits the grid’s own column widths to the printable width and calls pageHeader / pageFooter once the page count is known — so “Page 2 of 5” is one line.
repeat header page numbers fit to width
Uses the grid above — press PDF.
import { ChangeDetectionStrategy, Component, viewChild } from '@angular/core';
import { OgeColumn, OgeGrid } from '@oge-ui/grid';
interface Employee {
id: number;
firstName: string;
lastName: string;
department: string;
city: string;
salary: number;
hireDate: string;
}
@Component({
selector: 'demo-root',
imports: [OgeColumn, OgeGrid],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<oge-grid #grid [data]="employees" keyField="id" [groupBy]="['department']">
<oge-column field="firstName" caption="First Name" />
<oge-column field="lastName" caption="Last Name" />
<oge-column field="city" caption="City" />
<oge-column field="salary" caption="Salary" dataType="number" totalSummary="sum" />
</oge-grid>
<button type="button" (click)="pdf()">PDF</button>
`,
})
export class Demo {
readonly employees = [
{ id: 1, firstName: 'Ali', lastName: 'Yılmaz', department: 'Engineering', city: 'İstanbul', salary: 8400, hireDate: '2019-03-11' },
{ id: 2, firstName: 'Ayşe', lastName: 'Kaya', department: 'Sales', city: 'Ankara', salary: 7200, hireDate: '2020-07-02' },
{ id: 3, firstName: 'Mehmet', lastName: 'Demir', department: 'Engineering', city: 'İzmir', salary: 9100, hireDate: '2018-01-23' },
{ id: 4, firstName: 'Zeynep', lastName: 'Şahin', department: 'Finance', city: 'İstanbul', salary: 6800, hireDate: '2021-11-15' },
{ id: 5, firstName: 'Emre', lastName: 'Çelik', department: 'Support', city: 'Bursa', salary: 5400, hireDate: '2022-05-09' },
];
private readonly grid = viewChild.required<OgeGrid<Employee>>('grid');
protected async pdf(): Promise<void> {
const { exportGridToPdf } = await import('@oge-ui/grid/export-pdf');
await exportGridToPdf(this.grid(), {
filename: 'employees.pdf',
title: 'Employees by department',
orientation: 'portrait',
// the header repeats on every page; widths follow the grid, fitted to the page
pageHeader: () => 'OGE UI · HR report',
pageFooter: ({ pageNumber, pageCount }) => `Page ${pageNumber} of ${pageCount}`,
customizeCell: ({ field, style }) => {
if (field === 'salary' && style) style.alignment = 'end';
return undefined;
},
});
}
}Notes
getExportData(options)is the shared source: rows, columns with width / alignment / pin side / band, and the structureditems(group rows, group footers, the total row) the builders read — usebuildExcelWorkbook/buildPdfDocumentdirectly for a custom pipeline.visibleColumnsOnly: falseadds the hidden columns; columns hidden only by the responsive width pass always export.groups: false/summaries: falsegive a flat sheet; CSV is always flat.customizeCellstill rewrites a data cell’s value and can now restyle it through its mutablestyle;cellStylealso reaches header, group and summary lines.