Export Angular Material Tables

Data is everything and everywhere. UX to present complicated information is cruicial for data centric use-cases. Most of the time good old tables suffice and almost always requirement specifications imply “Table Export” feature. If you are lucky the component suit you develope with handles table exporting boilerplate otherwise it would be the time to invent that wheel.

In this post I will talk a bit about how my mat-table-exporter & cdk-table-exporter packages (Table Exporter for Angular Material) are born, about their implementation details and finally how to use mat-table-exporter.

Like one year ago when I first needed table exporting feature for one of my first Angular Material applications, I was frustrated not to find any good solution of such an essential thing in public repositories. Speaking of Angular Material team, they do not choose to support these kind of extra cases so it was left to community. The already published solutions and examples on the internet was either to export the native-table rendered in the page or to export the json array instead of the table itself. Doing these is really simple by employing SheetJS library. However my tables were paginated. Moreover I had to export the tables keeping how they seem in the web page. This means if I were to export my json array that feeds the table’s datasource, I had to create a shown-in-the-page version of it and export it instead. This is how I decided to create a more inclusive solution to this problem.

import { Component, ViewChild, ElementRef } from '@angular/core';
import * as XLSX from 'xlsx';
export class AppComponent {
@ViewChild('TABLE', { static: true }) table: ElementRef;
exportTable() {
const ws: XLSX.WorkSheet = XLSX.utils.table_to_sheet(this.table.nativeElement);
const wb: XLSX.WorkBook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
XLSX.writeFile(wb, 'Exported_File.xlsx');
}
}
view raw AppComponent.ts hosted with ❤ by GitHub
Example of how you can easily export your one page Angular Material Table with SheetJS

mat-table-exporter

My solution provides the ability to choose which columns will end up in the file, support for paginated tables with server-side or client-side pagination and exporting in popular file formats other than xls and xlsx, like csv, json, txt with costum delimiters.

The de facto of excel creation in javascript is SheetJS. So my exporter also depends on it and FileSaverJS for cross-browser file saving. However when you look at the dependencies of mat-table-exporter in npm, the only dependency you will see is cdk-table-exporter. This is because the core functionality is inside cdk-table-exporter and material specific coding is done inside mat-table-exporter.

How does it export paginated tables?

If a table is paginated in Angular, only the shown page is rendered and gets to the DOM. So all you can access is only the currently shown data. If you are to export the table with all it’s pages then you have to iterate over them and collect the entire data set before exporting. This may sound like a naive approach however Angular CDK/Material does not provide any better way of doing this. Maybe by applying some sort of reflection, getting the template html of table and evaluating it’s interpolations towards the json array is possible at runtime, that way we can export the json array just as it is represented in the table without iterating over pages getting them rendered one by one. Currently mat-table-exporter & cdk-table-exporter are implemented based on the paging approach.

Please feel free to share your ideas and suggestions on this matter, I’m in favor of changing this implementation mainly because of performance related reasons.

import { AfterViewInit, Directive, Host, Optional, Renderer2, Self } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatTable, MatTableDataSource } from '@angular/material/table';
import { CdkTableExporter, DataExtractorService, ServiceLocatorService } from 'cdk-table-exporter';
import { Observable } from 'rxjs';
@Directive({
selector: '[matTableExporter]', // renamed selector but kept old version for backwards compat.
exportAs: 'matTableExporter'
})
export class MatTableExporterDirective extends CdkTableExporter implements AfterViewInit {
/**
* Overriding ngAfterViewInit of TableExporter
*/
ngAfterViewInit(): void {
this.exportStarted.subscribe(_ => {
this.enablePaginator(false);
});
this.exportCompleted.subscribe(_ => {
this.enablePaginator(true);
});
}
constructor(
renderer: Renderer2,
serviceLocator: ServiceLocatorService,
dataExtractor: DataExtractorService,
@Host() @Self() @Optional() table: MatTable<any>
) {
super(renderer, serviceLocator, dataExtractor, table);
}
/**
* MatTable implementation of getPageCount
*/
public getPageCount(): number {
return this.getPaginator().getNumberOfPages();
}
/**
* MatTable implementation of getCurrentPageIndex
*/
public getCurrentPageIndex(): number {
return this.getPaginator().pageIndex;
}
/**
* MatTable implementation of goToPage
*/
public goToPage(index: number): void {
this.getPaginator().pageIndex = index;
this.getPaginator()._changePageSize(this.getPaginator().pageSize);
}
/**
* MatTable implementation of getPageChangeObservable
*/
public getPageChangeObservable(): Observable<any> {
return this.getPaginator().page;
}
private getPaginator(): MatPaginator {
return (this._cdkTable.dataSource as MatTableDataSource<any>).paginator;
}
private enablePaginator(value: boolean) {
if (this.getPaginator()) {
this.getPaginator().disabled = !value;
this.getPaginator()._changePageSize(this.getPaginator().pageSize);
}
}
}

As you can see MatTableExporterDirective is an implementation of abstract CdkTableExporter. It’s abstract methods are mainly about how the pagination should work and the way mat-table-exporter implements them is based on the hosting matTable component’s datasource. This makes the exporter compatible with any material table. This is one of the most powerfull aspects of mat-table-exporter.

How to use?

  • First install mat-table-exporter from NPM
npm install --save mat-table-exporter
  • Import MatTableExporterModule inside your NgModule
import { MatTableExporterModule } from 'mat-table-exporter';
  • Apply the directive selector to your table
<mat-table matTableExporter [dataSource]="dataSource" #exporter="matTableExporter">

Now you can export the table by calling exporter.exportTable(exportType, options) method:

<button mat-button (click)="exporter.exportTable('csv')"></button>

You can take a look at the StackBlitz example for the demonstration of all the features.

And don’t forget to Star the repo on GitHub.

2 thoughts on “Export Angular Material Tables

  1. Dear

    Your component is amazing, I use a lot, I have just a question, where i change the delimiter? Do you have an example?

    Liked by 1 person

Leave a comment