Skip to main content

Export to Excel in Angular 8 using ExcelJS

Two important open source libraries available to read/write an excel in the client-side web applications are :

Both libraries are used to read, manipulate and write spreadsheet data and styles to XLSX and JSON.

Initially, I had used XLSX & XLSX-style libraries to create and format an excel from JSON but the adverse point of this open-source library is it doesn’t provide any feature to add an image in excel (add image feature is available in pro version), which was the big requirement for my excel.

Later on, I found ExcelJS, which is really easy to use, provide almost all the features of Excel, and the main thing is it also provide add image feature.

Export to Excel in Angular 8 using ExcelJS

Create a Angular 8 Project

Use below command to create a new Angular 6 project with Angular CLI.

ng new angular-exceljs-example

Install ExcelJS Library

npm install --save exceljs@1.12.0

Important

The latest version of 

exceljs
 (version 
3.4.0
 while writing the article) uses the 
@types/node@^10.12.0
 while angular 8 uses 
@types/node@~8.9.4
. Because of this, you might face the following error while compiling an application :

ERROR in node_modules/exceljs/index.d.ts:1661:34 - error TS2503: Cannot find namespace 'NodeJS'.
dictionary: Buffer | NodeJS.TypedArray | DataView | ArrayBuffer; // deflate/inflate only, empty dictionary by default

We can fix this by upgrading the 

@types/nodes
 to 
10.12.0
 but because angular 8 doesn’t use this version I will not recommend doing so, So until we required any specific feature which is released in latest 
exceljs
 version, I will recommend you to use 
exceljs@1.12.0
 for angular 8 applications.

If you find any issue in angular 8 with 

exceljs
, Refer to the comment section below which contains some useful solutions. However, if you don’t find any solution please add your issue in new comment.

Update tsconfig.js

ExcelJS is generally used for server side web applications in node. Here we want to use it in client side Angular application. for that we need to set 

compilerOptions
 in 
tsconfig.json
 as shown below :

"compilerOptions": {
...
"paths": {
"exceljs": [
"node_modules/exceljs/dist/exceljs.min"
]
}
}
  • Note

If you are using Angular 4 or 5 you need to use the following path in 

tsconfig.json

"paths": {
"exceljs": [
"../node_modules/exceljs/dist/es5/exceljs.browser"
]
}

In Linux, It is not able to find 

exceljs.browser
 or 
exceljs.min
 from 
tsconfig.js
 So remove the path from 
tsconfig
 and import ExcelJS like this:

import * as Excel from "exceljs/dist/exceljs.min.js";
import * as ExcelProper from "exceljs";
let workbook: ExcelProper.Workbook = new Excel.Workbook();

Install file-saver

FileSaver.js is the solution to saving files on the client-side and is perfect for web apps that need to generate files, or for saving sensitive information that shouldn’t be sent to an external server.

It implements the 

saveAs()
 FileSaver interface in browsers that do not natively support it.

Install file-saver library using following command

npm install --save file-saver

Environment setup is done. Now lets start to build an excel.

We will create a separate service in our project called 

excel.service.ts
,  you can create it using below command

ng generate service excel

Import ExcelJS and FileSaver

In 

excel.service.ts
, add the following import statements.

import { Workbook } from 'exceljs';
import * as fs from 'file-saver';

Create a separate method and data varibales.

In 

excel.service.ts
, We will create a separate method called 
generateExcel()
.

In this method, I have created some data variables as below, We will export these data in excel sheet.

Note: You can pass data from the component as a parameter in 

generateExcel()
 and generate a dynamic excel sheet.

const title = 'Car Sell Report';
const header = ["Year", "Month", "Make", "Model", "Quantity", "Pct"]
const data = [
[2007, 1, "Volkswagen ", "Volkswagen Passat", 1267, 10],
[2007, 1, "Toyota ", "Toyota Rav4", 819, 6.5],
[2007, 1, "Toyota ", "Toyota Avensis", 787, 6.2],
[2007, 1, "Volkswagen ", "Volkswagen Golf", 720, 5.7],
[2007, 1, "Toyota ", "Toyota Corolla", 691, 5.4],
...
];

Create Workbook and Add Worksheet

Create a new workbook and add a new worksheet using 

addWorksheet()
 method of Workbook.

let workbook = new Workbook();
let worksheet = workbook.addWorksheet('Car Data');

Add Row and format the fonts.

We will use 

addRow()
 method of worksheet object. to add a row in a worksheet. as below,

// Add new row
let titleRow = worksheet.addRow([title]);
// Set font, size and style in title row.
titleRow.font = { name: 'Comic Sans MS', family: 4, size: 16, underline: 'double', bold: true };
// Blank Row
worksheet.addRow([]);
//Add row with current date
let subTitleRow = worksheet.addRow(['Date : ' + this.datePipe.transform(new Date(), 'medium')]);

Add Image in worksheet

To add an image in excel we need to add base64 of an image. I have saved the base64 of logo image in a separate file called 
carlogo.js
as below.
export const logoBase64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfQAAAFKCAMAAADcyF29AAABxVBMVEVHcEwJCAggFxEBAQE2KyQAAAA0LScAAAAAAAA1LysXEQ0EBAQFBAMDAwMLCQgGBQUFBAOEQhUHBwZjQSuScFoVFRZvNAx5NghcOyaudU8yMDBrNhOiViMZFhXEdD3Ef0+4ZzFISUdSJwliMA6BPA6lVR8CAgEDAgQPDhANDgsKCQoVFhPObi4SERS2VxcE.......";
To use it in generateExcel() method, We need to import carlogo.jsin excel.service.ts as below,
import * as logoFile from './carlogo.js';
Now, add an image in a worksheet as below,
let logo = workbook.addImage({
base64: logoFile.logoBase64,
extension: 'png',
});
worksheet.addImage(logo, 'E1:F3');
workbook.addImage(image)
 creates an image object and returns the image id, that image id we will use to place image in the worksheet using 
worksheet.addImage(imageId, cellRange)
. The coordinates calculated from the range will cover from the top-left of the first cell to the bottom right of the second.

Merge Cells

We can merge cells using 

worklist.mergeCells(cellRange)
 method, as below,

worksheet.mergeCells('A1:D2');

The coordinates calculated from the range will cover from the top-left of the first cell to the bottom right of the second.

Add Data with Header & Conditional Formatting

We will add a header row for car data records with a background color, as below,

//Add Header Row
let headerRow = worksheet.addRow(header);
// Cell Style : Fill and Border
headerRow.eachCell((cell, number) => {
cell.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFFFFF00' },
bgColor: { argb: 'FF0000FF' }
}
cell.border = { top: { style: 'thin' }, left: { style: 'thin' }, bottom: { style: 'thin' }, right: { style: 'thin' } }
});

ExcelJS directly doesn’t support conditional formatting, but we can achieve this functionality by assigning style based on required condition in angular, as below,

// Add Data and Conditional Formatting
data.forEach(d => {
let row = worksheet.addRow(d);
let qty = row.getCell(5);
let color = 'FF99FF99';
if (+qty.value < 500) {
color = 'FF9999'
}
qty.fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: color }
}
}
);

Note: Using 

worklist.addRows(records: any[])
 method we can add multiple rows. as an example,

worksheet.addRows(data);

But in our example, we want to perform cell styling based on a conditional check, So we have saved the individual row using 

worklist.addRow(record)
 method.

Same way, you can add other rows like footer and additional information.

Export file using FileSaver

Now our workbook is ready to export. We can export it using 

saveFile()
 method of file-saver, as shown below

workbook.xlsx.writeBuffer().then((data) => {
let blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
fs.saveAs(blob, 'CarData.xlsx');
});

Final Code Review

Note : I have added 

Generate Excel
 button in 
AppComponent
 which will call 
genetateExcel()
 method of 
excel.service.ts
.

tsconfig.js
app.component.ts
app.component.html
excel.service.ts
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es5",
"paths": {
"exceljs": [
"node_modules/exceljs/dist/exceljs.min"
]
},
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
]
}
}







Comments

Popular posts from this blog

How to use Ngx-Charts in Angular ?

Charts helps us to visualize large amount of data in an easy to understand and interactive way. This helps businesses to grow more by taking important decisions from the data. For example, e-commerce can have charts or reports for product sales, with various categories like product type, year, etc. In angular, we have various charting libraries to create charts.  Ngx-charts  is one of them. Check out the list of  best angular chart libraries .  In this article, we will see data visualization with ngx-charts and how to use ngx-charts in angular application ? We will see, How to install ngx-charts in angular ? Create a vertical bar chart Create a pie chart, advanced pie chart and pie chart grid Introduction ngx-charts  is an open-source and declarative charting framework for angular2+. It is maintained by  Swimlane . It is using Angular to render and animate the SVG elements with all of its binding and speed goodness and uses d3 for the excellent math functio...

Understand Angular’s forRoot and forChild

  forRoot   /   forChild   is a pattern for singleton services that most of us know from routing. Routing is actually the main use case for it and as it is not commonly used outside of it, I wouldn’t be surprised if most Angular developers haven’t given it a second thought. However, as the official Angular documentation puts it: “Understanding how  forRoot()  works to make sure a service is a singleton will inform your development at a deeper level.” So let’s go. Providers & Injectors Angular comes with a dependency injection (DI) mechanism. When a component depends on a service, you don’t manually create an instance of the service. You  inject  the service and the dependency injection system takes care of providing an instance. import { Component, OnInit } from '@angular/core'; import { TestService } from 'src/app/services/test.service'; @Component({ selector: 'app-test', templateUrl: './test.component.html', styleUrls: ['./test.compon...

How to solve Puppeteer TimeoutError: Navigation timeout of 30000 ms exceeded

During the automation of multiple tasks on my job and personal projects, i decided to move on  Puppeteer  instead of the old school PhantomJS. One of the most usual problems with pages that contain a lot of content, because of the ads, images etc. is the load time, an exception is thrown (specifically the TimeoutError) after a page takes more than 30000ms (30 seconds) to load totally. To solve this problem, you will have 2 options, either to increase this timeout in the configuration or remove it at all. Personally, i prefer to remove the limit as i know that the pages that i work with will end up loading someday. In this article, i'll explain you briefly 2 ways to bypass this limitation. A. Globally on the tab The option that i prefer, as i browse multiple pages in the same tab, is to remove the timeout limit on the tab that i use to browse. For example, to remove the limit you should add: await page . setDefaultNavigationTimeout ( 0 ) ;  COPY SNIPPET The setDefaultNav...