22

I am using angular material data table to display data in a tabular format. I need to include a functionality which exports tabular data to excel. I am not able to find any documents which will help me to export the data. Can you please let me know how to export data to excel in angular which uses angular material data table.

I tried using XLSX.utils and facing "Bad range (0): A1:A0 at check_ws" issue.

Location.component.html

<div class="example-container" #TABLE> 

  <mat-table #table [dataSource]="dataSource" matSort matSortActive="locationName" matSortDirection="asc" matSortDisableClear>
    <ng-container matColumnDef="locationName">
      <mat-header-cell *matHeaderCellDef mat-sort-header>Location Name </mat-header-cell>
      <mat-cell *matCellDef="let location"> {{location.locationName}} </mat-cell>
    </ng-container>
    <ng-container matColumnDef="address">
      <mat-header-cell *matHeaderCellDef>Address </mat-header-cell>
      <mat-cell *matCellDef="let location"> {{location.address}} </mat-cell>
    </ng-container>
    <ng-container matColumnDef="city">
      <mat-header-cell *matHeaderCellDef mat-sort-header> City </mat-header-cell>
      <mat-cell *matCellDef="let location"> {{location.city}} </mat-cell>
    </ng-container>
    <ng-container matColumnDef="country">
      <mat-header-cell *matHeaderCellDef mat-sort-header>Country </mat-header-cell>
      <mat-cell *matCellDef="let location"> {{location.country}} </mat-cell>
    </ng-container>
    <ng-container matColumnDef="zipcode">
      <mat-header-cell *matHeaderCellDef>ZipCode </mat-header-cell>
      <mat-cell *matCellDef="let location"> {{location.zipcode}} </mat-cell>
    </ng-container>
    <ng-container matColumnDef="phone">
      <mat-header-cell *matHeaderCellDef>Phone </mat-header-cell>
      <mat-cell *matCellDef="let location"> {{location.phone}} </mat-cell>
    </ng-container>
    <ng-container matColumnDef="timezone">
      <mat-header-cell *matHeaderCellDef> TimeZone </mat-header-cell>
      <mat-cell *matCellDef="let location"> {{location.timezone}} </mat-cell>
    </ng-container>
    <ng-container matColumnDef="action">
      <mat-header-cell *matHeaderCellDef> Action </mat-header-cell>
      <!-- <mat-cell *matCellDef="let location"> {{location.timezone}} </mat-cell> -->
      <mat-cell *matCellDef="let location">
      <a href ="#" class="btn Action-Tab" >Edit</a>&nbsp;&nbsp;
          <a href ="#" class="btn Action-Tab" >Delete</a>
        </mat-cell>
    </ng-container>
    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns;">
    </mat-row>
  </mat-table>

  <mat-paginator [pageSizeOptions]="[10, 20, 50,100]"></mat-paginator>

</div>
<button mat-raised-button color="primary" (click)="ExportTOExcel()">Export as Excel</button>

Location.component.ts

import { Component, OnInit, OnDestroy , ViewChild,ElementRef} from '@angular/core';
import { ILocation } from '../../Ilocation';
import { LocationService } from '../../services/location.service';
import { DataTableResource } from 'angular5-data-table';
import { Subscription } from 'rxjs';
import {MatPaginator, MatSort, MatTableDataSource} from '@angular/material';
import {DataSource} from '@angular/cdk/table';
import * as XLSX from 'xlsx';
// import { CdkTableModule } from '@angular/cdk/table';

@Component({
  selector: 'app-location',
  templateUrl: './location.component.html',
  styleUrls: ['./location.component.css']
})
export class LocationComponent implements OnInit , OnDestroy{
  errorMessage: string;
  filterBy : string;
  locations: ILocation[];
  items : ILocation[]=[];
  itemCount :number = 0;
  subscription:Subscription;
  limits = [5, 10, 20, 80];
  tableResource : DataTableResource<ILocation>;
  displayedColumns = ['locationName', 'address', 'city', 'country','zipcode', 'phone','timezone','action'];
  // dataSource: MatTableDataSource<ILocation>;
  dataSource;
  @ViewChild(MatPaginator) paginator: MatPaginator;
  @ViewChild(MatSort) sort : MatSort;
  @ViewChild('TABLE',{ read: ElementRef }) table: ElementRef;

  constructor( private locationService: LocationService) {

   }

   applyFilter(filterValue: string) {
    filterValue = filterValue.trim(); // Remove whitespace
    filterValue = filterValue.toLowerCase(); // Datasource defaults to lowercase matches
    this.dataSource.filter = filterValue;
  }

  ngOnInit() {
    this.subscription = this.locationService.getLocations()
    .subscribe(locations =>{
       this.locations = locations;
       this.dataSource = new MatTableDataSource(locations);
       this.dataSource.sort = this.sort;
       this.dataSource.paginator = this.paginator;
       this.dataSource.table = this.table;
      },
      error => this.errorMessage = <any>error);           
  }

  ngOnDestroy(){
    this.subscription.unsubscribe();

  }

  ExportTOExcel()
  {
    console.log("export");
    this.table.nativeElement.style.background = "red";
    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');

    /* save to file */
    XLSX.writeFile(wb,'SheetJS.xlsx');
    console.log("exported");

  }

}
Veda
  • 577
  • 1
  • 6
  • 22

9 Answers9

33

You can use xlsx for exporting table as excel.
usage
Execute npm i xlsx

HTML:

<div class="example-container mat-elevation-z8 " #TABLE>
  <table mat-table #table [dataSource]="dataSource">

    <!--- Note that these columns can be defined in any order.
          The actual rendered columns are set as a property on the row definition" -->

    <!-- Position Column -->
    <ng-container matColumnDef="position">
      <th mat-header-cell *matHeaderCellDef> No. </th>
      <td mat-cell *matCellDef="let element"> {{element.position}} </td>
      //..................................rest of the html
    <button mat-raised-button color="primary" (click)="exportAsExcel()">Export as Excel</button></div>

In your Component

import {Component,ViewChild, ElementRef} from '@angular/core';
 import * as XLSX from 'xlsx';
//......
    export class AppComponent  {
      @ViewChild('TABLE') table: ElementRef;
    exportAsExcel()
    {
      const ws: XLSX.WorkSheet=XLSX.utils.table_to_sheet(this.table.nativeElement);//converts a DOM TABLE element to a worksheet
      const wb: XLSX.WorkBook = XLSX.utils.book_new();
      XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');

      /* save to file */
      XLSX.writeFile(wb, 'SheetJS.xlsx');

    }
    }

DEMO

Vikas
  • 11,859
  • 7
  • 45
  • 69
  • 1
    I am using mat-table for eg: Location Name {{location.locationName}} So where do I specify the table name ? @ViewChild('TABLE') table: ElementRef?? --Do I have to specify the table name replacing 'TABLE' – Veda May 17 '18 at 19:22
  • Did you Refer the provided demo ? – Vikas May 17 '18 at 19:23
  • 2
    I am getting this error "Cannot read property 'nativeElement' of undefined at LocationComponent.ExportTOExcel" – Veda May 17 '18 at 20:05
  • I changed @ViewChild('TABLE') table: ElementRef; to this @ViewChild('table', {read: ElementRef}) table: ElementRef; Now I am facing this isuue : " Bad range (0): A1:A0 at check_ws (xlsx.js:12006) at check_wb (xlsx.js:13743) at writeSync (xlsx.js:19589) " Can you pls help!! – Veda May 17 '18 at 20:27
  • Mate, I have provided you a working stackBlitz with entire code did you see that? go to app folder and look table-basic-example.ts and html file – Vikas May 18 '18 at 03:45
  • apart from it if you need my Help you need to share code kindly update your question – Vikas May 18 '18 at 03:48
  • Dont you think I checked the working code before posting here? Indeed I did. – Veda May 18 '18 at 13:01
  • Updated the question and shared the code. Plz have a look and let me know. Thanks – Veda May 18 '18 at 15:35
  • `this.table.nativeElement.style.background = "red";` what's this for? – Vikas May 18 '18 at 15:58
  • Your code looks fine mate I am wondering why `"Cannot read property 'nativeElement' of undefined at ` this error is cropped up – Vikas May 18 '18 at 16:01
  • Could you provide a stackblitz – Vikas May 18 '18 at 16:02
  • I got this error""Cannot read property 'nativeElement' of undefined " because I didnt include #TABLE id in the div . Yep, I will provide a stackblitz – Veda May 18 '18 at 16:31
  • I have a font-awesome issue with stackblitz, https://github.com/veda123/Angular5 -- this is my github link (you can look for data table in location.component) . Can you please help me out. I am struck with this issue. thanks – Veda May 21 '18 at 02:02
  • 3
    No, I won't look into it, I gave you the answer to your issue, in fact, a working stackblitz, I'm not your personal code Monkey. – Vikas May 21 '18 at 04:37
  • Mate, I never asked to be personal code Monkey! I provided the code since you asked for it!! Anyways thanks for providing working stackblitz!! – Veda May 23 '18 at 13:46
  • @Laxman Pai I would really like to know what made you tell that *`THis is not a proper answer for mat-table.`* I have provided a working stackblitz with the answer in which this solution works as desired ,Your job is to copy paste n tweak it accordingly If you are not able to do that then better go clear you basics get some idea and then try it out, or Post a question on SO and lemme have a look at attempt made by you what error u get, why it is not working? – Vikas Jun 13 '18 at 10:35
  • @Vikas Your solution works only on stackblitz, try it on your local it will not work because angular version 5 and more does not support
    instead we have to write . Here is the tricky part, xlsx does not understand , it only works with . So your solution is only fit for version < 5. I do not know why stackblitz does not throw error probably they have not updated there compiler.
    – Gagan Jaura Jul 04 '18 at 07:56
  • check it out https://stackoverflow.com/questions/50970632/how-to-export-excel-file-from-angular-material-5-mat-table-angular-material-tab/51157054 – Gagan Jaura Jul 04 '18 at 07:57
  • @GaganJaura Well I have not tested it with material version 5, will get back to you soon:) – Vikas Jul 04 '18 at 08:16
  • @GaganJaura okay I have Verified it with latest version of material v6 and it works fine `
    ` was introduced in material version 6 check out [stackblitz](https://stackblitz.com/angular/pxgvnklgjjkg)
    – Vikas Jul 04 '18 at 08:24
  • @GaganJaura go through the latest version of material [DataTable](https://material.angular.io/components/table/overview) If you are facing any issue with exporting material V6 dataTable fork my stackblitz and share it:) – Vikas Jul 04 '18 at 08:28
  • @Vikas I crossed with material guys they have corrected the issue in V6, this issue is resolved. Thanks for the update. But still your solution will not work with V5. Veda has to upgrade to V6. – Gagan Jaura Jul 04 '18 at 12:57
  • it exports only the 1st page records if we have implemented mat-paginator in it, can you please help with that – Varsha Bommarabettu Aug 24 '18 at 10:43
  • @VarshaBommarabettu Gimme sometime I will have a look into it and revert back – Vikas Aug 24 '18 at 10:46
  • Is it possible ?? or should we send JSON obj? – Varsha Bommarabettu Aug 24 '18 at 11:18
  • @VarshaBommarabettu Send Json Object use `json_to_sheet` method as mentioned in docs – Vikas Aug 24 '18 at 12:36
  • @Vikas Yes, I had tried that, but json data contains crude data [ including Ids and all ] its better to show only table data.. – Varsha Bommarabettu Aug 24 '18 at 12:40
  • @VarshaBommarabettu Then Create an Object with the properties you want in your xls apart from it I would suggest you to post a question on SO so that community can help you with it and don't forget to add relevant code ;) – Vikas Aug 24 '18 at 12:43
  • I am working on excel report and I need to display image on export to excel in angular. But it is not display on above code. plz anyone know how to display image on export to excel in angular . – user7125929 Mar 04 '19 at 03:58
  • @Vikas does it really work in mat-table with paginator? – klaydze Nov 25 '19 at 14:49
  • @klaydze I have not tested it with pagination? Are you facing any issue? – Vikas Nov 25 '19 at 15:29
  • @Vikas, well I tried it in mat-table with pagination and it only export the first page of the table and not the entire data of the table. Not sure if there is a properly implementation to export all the data based on table. – klaydze Nov 26 '19 at 05:50
  • @klaydze I get it, It will only export the items currently in the view as we are querying the dom and working on the data present in the dom. – Vikas Nov 26 '19 at 07:57
  • @klaydze Let me see if I can figure out a way to export data with pagination – Vikas Nov 26 '19 at 07:58
  • @Vikas appreciate your help. Thanks! – klaydze Nov 26 '19 at 08:11
  • Vikas, did you manage to figure out a way to export with the whole data source? – Mitulát báti Aug 31 '20 at 14:28
  • @Mitulátbáti Nope, No luck – Vikas Sep 01 '20 at 10:49
  • 2
    Thanks for pointing out that I need to run the export on the containing `
    `, not the `` itself!
    – Elias Strehle Jun 07 '21 at 07:17
  • someone know, how to format cell to have all content cell align on right for example ? (using @Vikas example) – LinebakeR Jan 20 '22 at 16:47
14

Export to excel is easy to do with something like this:

exportExcel() {
    const workSheet = XLSX.utils.json_to_sheet(this.dataSource.data, {header:['dataprop1', 'dataprop2']});
    const workBook: XLSX.WorkBook = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(workBook, workSheet, 'SheetName');
    XLSX.writeFile(workBook, 'filename.xlsx');
}

Call it from button:

<button (click)="exportExcel()">Export</button>
Jovo Skorupan
  • 237
  • 2
  • 5
  • 1
    Nice trick. I would love to play with JSON rather than with HTML – Biswakalyan Feb 19 '20 at 11:42
  • this solution didn't need an @ViewChild or #TABLE identifier. replace this.dataSource.data with your dataSource. – Robert Price May 20 '20 at 12:45
  • This works in Angular 12 and with this answer https://stackoverflow.com/a/59979858/17960636 you can hide the columns you dont want to show. – Chris Aug 20 '22 at 18:59
  • This answer is so easy and perfect. I just extended this one a little bit to modify columns in the exported table for my wish. Might be useful for some one. `const workSheet = XLSX.utils.json_to_sheet( this.results.map((item) => { return { OrderNumber: item.orderNumber, CompensationID: item.returnOrders[0].compensationId, ReturnReasonID: item.returnOrders[0].returnReasonId, Status: item.status === "failed-return" ? "Failure" : "Success", }; }) );` – Shafeeque Jan 09 '23 at 11:03
13

It is a late reply but, you can use mat-table-exporter which utilizes xlsx sheetjs and provides paginated table export as excel, csv, json and txt formats.

talhature
  • 2,246
  • 1
  • 14
  • 28
  • This is working fine but it is not exporting footer of the mat-table (*matFooterCellDef) – Muhammad Ahsan Aug 21 '19 at 15:24
  • 1
    After the latest update footercell export is also supported – talhature Sep 03 '19 at 10:13
  • What if i have 5 mat-tables in my page and i want to export to excel by just 1 click of button into 1 Excel with different Tabs in excel. Is it possible ? – Krunal Shah Nov 14 '19 at 06:49
  • Currently it is not possible but it is in our backlogs – talhature Nov 14 '19 at 07:31
  • I tried the mat-table-expoter, it's easy to use. But, not sure how to set the width of the column to fit with the content. I use the XLSX, I can adjust the column with but since my table has a pagination, I'm not sure how to export it. It only export the first data in the table. – klaydze Nov 22 '19 at 02:59
  • @klaydze you can make use of columnWidths check the mat-table-exporter docs. – talhature Apr 09 '20 at 18:10
  • 1
    @talhature I am using mat-table-exporter, it works great, however, it only downloads the items listed on the page and not all items. So if the page is displaying 10 items, it only downloads 10 items, is there any setting so it can download all items? – C_sharp Sep 01 '20 at 03:50
  • @C_sharp thanks for letting me know. Could you please raise an issue on https://github.com/HalitTalha/ng-material-extensions giving more details about your case? It should export all the items, it supports pagination, so we need to look closer what's going wrong in your case. – talhature Sep 01 '20 at 07:41
  • @talhature same problem here, only the displayed items are exported. I'm using ^10.2.4 mte version with angular version of 12. – Mitulát báti Nov 14 '21 at 16:24
4

If you are rendering the Material flex table by using <mat-table>, <mat-header-cell>, <mat-cell>. The XLSX.utils.table_to_book to table won't work.

You can view the complete guide and working example as:

Guide: https://trungk18.com/experience/angular-material-data-table-export-to-excel-file/

Stackblitz: https://stackblitz.com/edit/angular-material-table-export-excel-xlsx

<mat-table [dataSource]="dataSource" class="mat-elevation-z8">
  <ng-container matColumnDef="name">
    <mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
    <mat-cell *matCellDef="let element"> {{element.name}} </mat-cell>
  </ng-container>

  <ng-container matColumnDef="symbol">
    <mat-header-cell *matHeaderCellDef> Symbol </mat-header-cell>
    <mat-cell *matCellDef="let element"> {{element.symbol}} </mat-cell>
  </ng-container>

  <mat-header-row *matHeaderRowDef="matColumns"></mat-header-row>
  <mat-row *matRowDef="let row; columns: matColumns;"></mat-row>
</mat-table>

You have to use XLSX.utils.json_to_sheet as to export the array to xlsx. It will be much more flexible and easy for you.

exportArrayToExcel(arr: any[], name?: string) {
    let { sheetName, fileName } = getFileName(name);
    var wb = XLSX.utils.book_new();
    var ws = XLSX.utils.json_to_sheet(arr);
    XLSX.utils.book_append_sheet(wb, ws, sheetName);
    XLSX.writeFile(wb, `${fileName}.xlsx`);
}
trungk18
  • 19,744
  • 8
  • 48
  • 83
1

In my case, alternative to table_to_sheet is to use json_to_sheet. Since I don't know how to properly export the table (with pagination) and the filtered table, I leverage the json_to_sheet and in the dataSource instead of dataSource.data, I use the dataSource.filteredData.

So with this, it covers the ff:

  • Export table with pagination
  • Export table with filters
  exportToExcel() {
    let dataToExport = this.dataSource.filteredData
      .map(x => ({
        DisplayName: x.DisplayName,
        Name: x.Name,
        Type: x.Type == '0' ? 'Partial' : 'Full'
      }));

    let workSheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(dataToExport, <XLSX.Table2SheetOpts>{ sheet: 'Sheet 1' });
    let workBook: XLSX.WorkBook = XLSX.utils.book_new();

    // Adjust column width
    var wscols = [
      { wch: 50 },
      { wch: 50 },
      { wch: 30 }
    ];

    workSheet["!cols"] = wscols;

    XLSX.utils.book_append_sheet(workBook, workSheet, 'Sheet 1');
    XLSX.writeFile(workBook, `${this.exportToExcelFileName}.xlsx`);
  }
klaydze
  • 941
  • 14
  • 36
0

I was having the same issue and I found this solution, which worked for me...

(#1) Pay attention to div table wrapper, it MUST contain #TABLE

<div #TABLE> 
    <table mat-table>
       <!-- Your table code goes here -->
    </table>
</div>

(#2) Now, before the closing of your </table> or </mat-table>, make these changes...

<mat-header-row></mat-header-row> ==> <tr mat-header-row>...</tr>

<mat-row></mat-row> ==> <tr mat-row></tr>

At this point, If you lose some CSS style of your table, use below code to fix the style back...

tr.mat-header-row, tr.mat-row {
    display: flex;
}

Hope this can help you.

vinayofficial
  • 462
  • 1
  • 5
  • 21
0

Do the following steps in order:

  1. First put your ID in the HTML table tag.
  2. Then inject the Excel service into the constructor and write the Excel function.
  3. Then write the Excel service.

HTML:

 <table id="ExampleTable">
  .
  .
  .
</table>

COMPONENT.TS:

 constructor(private excel: ExcelService) {}

AND

exportExcel(): void {
  this.excel.exportExcel('ExampleTable');
}

ExcelService:

public exportExcel(tableId: string, name?: string): void {
  const timeSpan = new Date().toISOString();
  const prefix = name || 'ExportResult';
  const fileName = `${prefix}-${timeSpan}`;
  const targetTableElm = document.getElementById(tableId);
  const wb = XLSX.utils.table_to_book(targetTableElm, { sheet: prefix } as 
  XLSX.Table2SheetOpts);
  XLSX.writeFile(wb, `${fileName}.xlsx`);
}
-1
  exportTable() {
if (this.arrayname.length >= 1) { //here your array name which are display in table
    $('#exportable tr td').css('text-align', 'center'); //table formating
    const downloadLink = document.createElement('a');
    const table = document.getElementById('exportable');
    const tableHTML = table.outerHTML.replace(/ /g, '%20');
    var html = table.outerHTML;
    var url = 'data:application/vnd.ms-excel,' + escape(html); // Set your html table into url 
    downloadLink.href = 'data:' + url + ' ';
    downloadLink.download = 'tablename.xls'
    downloadLink.click();
} else {
    alert('table is empty')
}
-2

you need to make changes in your HTML file. instead of :

<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;">

Do it like,

<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>