3

I am trying to export JSON data to excel which has a dropdown list.

Is it possible to create the dropdown(with options) in excel if we pass an array as a cell value?

I am trying to use the inbuilt library XLSX to achieve this but not able to see data if I pass the array in the cell value.

Update: Jack Provided one library ExcelJS that supports the data validation but needs to do some configurations for that.

Is there any Angular Library that supports this feature?

Below is an example of the project which I tried. I would like to append multiple sheets based on data passed to the service and names of sheets will be taken from the objects.

https://stackblitz.com/edit/angular6-export-xlsx-b4al4p

5
  • 1
    unfortunately I have never used xlsx library. I have tried to go through the docs on how to add validation using the library but I seem not to find anything. I believe the solution is around adding validation but how to do it I have no clue Commented Apr 16, 2021 at 19:02
  • Are there any other libraries that support data validation? Commented Apr 16, 2021 at 19:03
  • 1
    Currently I unfortunately do not have any suggestion Commented Apr 16, 2021 at 19:11
  • @Owen Found one library (ExcelJS) which supports this but needs to manipulate data to be sent. Commented Apr 21, 2021 at 23:08
  • I recommend this post, it works using the same library XLSX stackoverflow.com/questions/60149667/… Commented Apr 24, 2021 at 0:18

3 Answers 3

4
+200

This is just an addition to @ZackReam solution with a focus on your current scenario

To begin, we first define a data structure

  workbookData = [
    {
      workSheet: "data 1",
      rows: [
        { eid: "1", ename: "John", esal: ["val 1", "val2", "val 3"] },
        { eid: "4", ename: "Parker", esal: ["val 1", "val2", "val 3"] },
        { eid: "5", ename: "Iron", esal: ["val 1", "val2", "val 3"] }
      ]
    },
    {
      workSheet: "data 2",
      rows: [
        { eid: "9", ename: "Doe", esal: ["val 1", "val2", "val 3"] },
        { eid: "10", ename: "Peter", esal: ["val 1", "val2", "val 3"] },
        { eid: "11", ename: "Man", esal: ["val 1", "val2", "val 3"] }
      ]
    }

Next we define a service to generate a workbook dynamically from the above data

import { Injectable } from "@angular/core";
import * as FileSaver from "file-saver";
import * as ExcelJS from "exceljs/dist/exceljs.min.js";

const EXCEL_TYPE =
  "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8";
const EXCEL_EXTENSION = ".xlsx";

@Injectable()
export class ExcelService {
  constructor() {}

  public async exportAsExcelFile(workbookData: any[], excelFileName: string) {
    const workbook = new ExcelJS.Workbook();

    workbookData.forEach(({ workSheet, rows }) => {
      const sheet = workbook.addWorksheet(workSheet);
      const uniqueHeaders = [
        ...new Set(
          rows.reduce((prev, next) => [...prev, ...Object.keys(next)], [])
        )
      ];
      sheet.columns = uniqueHeaders.map(x => ({ header: x, key: x }));

      rows.forEach((jsonRow, i) => {
        let cellValues = { ...jsonRow };

        uniqueHeaders.forEach((header, j) => {
          if (Array.isArray(jsonRow[header])) {
            cellValues[header] = "";
          }
        });
        sheet.addRow(cellValues);
        uniqueHeaders.forEach((header, j) => {
          if (Array.isArray(jsonRow[header])) {
            const jsonDropdown = jsonRow.esal;
            sheet.getCell(
              this.getSpreadSheetCellNumber(i + 1, j)
            ).dataValidation = {
              type: "list",
              formulae: [`"${jsonDropdown.join(",")}"`]
            };
          }
        });
      });
    });

    const buffer = await workbook.xlsx.writeBuffer();
    this.saveAsExcelFile(buffer, excelFileName);
  }

  private getSpreadSheetCellNumber(row, column) {
    let result = "";

    // Get spreadsheet column letter
    let n = column;
    while (n >= 0) {
      result = String.fromCharCode((n % 26) + 65) + result;
      n = Math.floor(n / 26) - 1;
    }

    // Get spreadsheet row number
    result += `${row + 1}`;

    return result;
  }

  private saveAsExcelFile(buffer: any, fileName: string): void {
    const data: Blob = new Blob([buffer], {
      type: EXCEL_TYPE
    });
    FileSaver.saveAs(
      data,
      fileName + "_export_" + new Date().getTime() + EXCEL_EXTENSION
    );
  }
}

The service will dynamically determine the headers and the columns to set as validation

Transforming the data

We can transform your data to this structure using below

  transform (data) {
    const noOfRowaToGenerate = 10;
    return data.map(({name, values}) => {
      const headers = values.reduce((prev, next) => 
        ({...prev, [next.header]: Array.isArray
        (next.value) ? next.value.map(({name}) => name): next.value}), {})
      return {
        workSheet: name,
        rows: Array(noOfRowaToGenerate).fill(headers)
      }
    })
  }
   workbookData = this.transform(this.data1)

Below is a sample demo

Sample Demo

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks a lot, Owen.. just wondering what changes I need to do to support the structure like this where the header is coming from object data1: any = [ { name: "data1", //sheet1 with name data1 values: [ { header: "eid", value: "" }, { header: "test", value: [{ name: "test1" }, { name: "test2" }] }, { header: "ename", value: "" }, { header: "esal", value: [{ name: "val" }, { name: "val1" }] } ] } ];
Will have a look in the morning and get back
this is a very good solution for the Bravo issue
Hi Owen, Can you please help me with this question? stackoverflow.com/questions/67571001/…
2

According to this issue in SheetJS (package: xlsx), data validation is a pro-only feature.

We offer this in the Pro compendium. Since companies have paid for the features already, it would be unfair to them if we turned around and made it available as open source.


Another library you could check out is ExcelJS (package: exceljs). It runs in the browser and has support for data validation. You would have to do a bit of manual mapping of your data to get your data in the format that the library expects, but it is possible.

Here is an example Stackblitz.

5 Comments

Thanks a lot, Just One question if I have a data structure like, How I can create excel dynamically based on the value have an array of objects? data: any = [ { header: "eid", value: "" }, { header: "test", value: [ { name: "test1" }, { name: "test2" } ] }, { header: "ename", value: "" }, { header: "esal", value: [ { name: "val" }, { name: "val1" } ] } ];
In this example excel header will as eid,test,ename, and esal and next row have dropdown validations if the value has an array.
@Bravo I have checked demo provided by ZackReam and it is working fantastically. How would you like me to help?
I have modified data where object have header and data and dropdown values (if available) as well as multiple sheets.
It is available in my stackblitz.
1

You can try do it this works using the same library exceljs and filesaver.js from you example

import { Workbook } from 'exceljs';
import * as fs from 'file-saver';
generateExcel(list,header) {
  let data:any = [];
  for(let i=0;i<list.length;i++){
    let arr = [list[i].requisitionId,list[i].applicationid, list[i].candidateid, list[i].unitcode];
    data.push(arr);
  }
  console.log(data);
  //Create workbook and worksheet
  let workbook = new Workbook();
  let worksheet = workbook.addWorksheet('Candidate Report');

  //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' } }
  })
  worksheet.getColumn(3).width = 30;
  data.forEach(d => {
    let row = worksheet.addRow(d);
  }
  );
  list.forEach((element,index) =>{
    worksheet.getCell('E'+(+index+2)).dataValidation = {
      type: 'list',
      allowBlank: true,
      formulae: ['"Selected,Rejected,On-hold"']
  };
  })
  //Generate Excel File with given name
  workbook.xlsx.writeBuffer().then((data) => {
    let blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
    fs.saveAs(blob, 'candidate.xlsx');
  })

}

My reference is this How to add data validation list in excel using javascript

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.