Angular clarity datagrid export filtered data to excel

193 Views Asked by At

I want to export filtered/sorted data(or whatever other actions applied to the table, ie: hide specific column) instead of exporting all the data. Is there a way to do it? I am fairly new to angular, if you could use simple language to explain that'll be great!

Below are my codes

excel.service.ts:

import { Injectable } from '@angular/core';
import * as FileSaver from 'file-saver';
import * as XLSX from 'xlsx';


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

@Injectable({
  providedIn: 'root'
})
export class ExcelService {

  constructor() { }

  public exportAsExcelFile(json: any[], excelFileName: string): void { 
    const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(json);  
    delete (worksheet[1])
    const workbook: XLSX.WorkBook = { Sheets: { 'data': worksheet }, SheetNames: ['data'] };  
    const excelBuffer: any = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });  
    this.saveAsExcelFile(excelBuffer, excelFileName);  
  }  
  private saveAsExcelFile(buffer: any, fileName: string): void {  
     const data: Blob = new Blob([buffer], {type: EXCEL_TYPE});  
     FileSaver.saveAs(data, fileName + '_' + new Date().toLocaleDateString() + EXCEL_EXTENSION);  
  }

}

table.component.ts:

showTCExportMenu=false
  showTEExportMenu=false
  exportAsXLSX(data, name, reimport=false):void {
    let table = data

    reimport? null: table.forEach(
                    table => { table.justification? table.justification = table.justification.replace(/<\/?(?!a)\w*\b[^>]*>/ig, ''): null,
                              table.evidence?  table.evidence = table.evidence.replace(/<\/?(?!a)\w*\b[^>]*>/ig, ''): null,
                              delete table.id, delete table.log
                              })


    // execu ? this.getTestExecu(this.uuid): this.getTestCat(this.uuid)

    this.excelService.exportAsExcelFile(table, name);
    this.showTCExportMenu=false
    this.showTEExportMenu=false
  }

table.component.html:

<div>
  <clr-datagrid  [(clrDgSelected)]="selected">
    <clr-dg-column
      *ngFor="let column of columns"
      [clrDgField]="getStringKey(column.key)">
      <ng-container *clrDgHideableColumn="{hidden: false}">
        {{column.title}}
      </ng-container>
      <clr-dg-filter
        *ngIf="column?.filter"
        [clrDgFilter]="getFilter(column)">
        <ng-container filterHost></ng-container>
      </clr-dg-filter>

    </clr-dg-column>
    <clr-dg-row
      *clrDgItems="let item of records"
      [clrDgItem]="item">
      <clr-dg-cell
        *ngFor="let column of columns"
        [innerHTML]="renderValue(column, item)">
      </clr-dg-cell>
      <clr-dg-row-detail
        [innerHTML]="expandRowRender && expandRowRender(item)"
        *clrIfExpanded>
      </clr-dg-row-detail>
    </clr-dg-row>

    <clr-dg-footer>
      <button class="btn" (click)="exportAsXLSX(tableData, 'example', true)">Export to excel</button>
      <clr-dg-pagination #pagination >
        <clr-dg-page-size [clrPageSizeOptions]="[5,10,20,30, 50, 100]">Users per page</clr-dg-page-size>
        {{pagination.firstItem + 1}} - {{pagination.lastItem + 1}}
        of {{pagination.totalItems}} records
      </clr-dg-pagination>
    </clr-dg-footer>
  </clr-datagrid>
</div>

here is where I get my data from api, catone.component.ts:

import { Component, OnInit } from '@angular/core';
import {TableColumns, TableData} from "../tableone/tableone.types";
import { faker } from '@faker-js/faker';
import {Observable, Subject} from "rxjs";
import {HttpClient} from "@angular/common/http";
import {ClrDatagridFilterInterface} from "@clr/angular";
import {AgefilterComponent} from "../agefilter/agefilter.component";
import {DatefilterComponent} from "../datefilter/datefilter.component";


interface InventoryData {
  test_name: any;
  description: any;
  status: string;
  test_level: string;
  test_category: string;
  test_type: string;
  test_approach: string;
  test_applicability: string;
  requirement_accountability: string;
  test_plan_accountability: string;
  trigger_type: string;
  applicable_metadata_statuses: string;
  assessment_level_targeted: string;
  comp_control_flag: string;
  testing_platform: string;
  effective_date: Date;
  test_retired_date: Date;
  metric_inclusion: string;
  policy_requirement: string
}



export  class AgeFilter implements ClrDatagridFilterInterface<InventoryData> {
  changes = new Subject<any>();
  public minAge!: number;
  public  maxAge!: number;
  accepts(person: InventoryData): boolean {
    return true
  }

  isActive(): boolean {
    return false
  }

}

export class BirthdayFilter implements ClrDatagridFilterInterface<InventoryData> {
  changes = new Subject<any>;
  value = '';
  accepts(item: InventoryData): boolean {
    return true
  }

  isActive(): boolean {
    return false
  }
}

@Component({
  selector: 'catone',
  templateUrl: './catone.component.html',
  styleUrls: ['./catone.component.scss']
})
export class CatoneComponent implements OnInit {
  columns: TableColumns<InventoryData> = [

    {
      title: 'Test_name',
      key: 'test_name'
    },
    {
      title: 'Description',
      key: 'description'
    },
    {
      title: 'Status',
      key: 'status'
    },
    {
      title: 'Test_level',
      key: 'test_level'
    },
    {
      title: 'Test_category',
      key: 'test_category'
    },
    {
      title: 'Test_type',
      key: 'test_type',
    },
    {
      title: 'Test_approach',
      key: 'test_approach',
    },
    {
      title: 'Test_applicability',
      key: 'test_applicability',
    },
    {
      title: 'Requirement_accountability',
      key: 'requirement_accountability',
    },
    {
      title: 'Test_plan_accountability',
      key: 'test_plan_accountability',
    },
    {
      title: 'Trigger_type',
      key: 'trigger_type',
    },
    {
      title: 'Applicable_metadata_statuses',
      key: 'applicable_metadata_statuses',
    },
    {
      title: 'Assessment_level_targeted',
      key: 'assessment_level_targeted',
    },
    {
      title: 'Comp_control_flag',
      key: 'comp_control_flag',
    },
    {
      title: 'Testing_platform',
      key: 'testing_platform',
    },
    {
      title: 'Effective_date',
      key: 'effective_date',
    },
    {
      title: 'Test_retired_date',
      key: 'test_retired_date',
    },
    {
      title: 'Metric_inclusion',
      key: 'metric_inclusion',
    },
    {
      title: 'Policy_requirement',
      key: 'policy_requirement',
    },

  ];
  tableData: TableData<InventoryData> | Observable<TableData<InventoryData>> = [];
  page = 1;
  pageSize = 10;
  total = 1000;
  constructor(private httpClient: HttpClient) { }


  renderExpand(p: InventoryData) {
    return `
      <pre>${'test name: ' + p.test_name}</pre>
    `
  }

  handlePageChange(page: number) {
    this.page = page;
    this.requestApi();
  }

  requestApi() {
    this.httpClient.get<InventoryData[]>(`myapi`)
      .subscribe(data => {
        this.tableData = data
      })
  }

  ngOnInit(): void {
    this.requestApi();
  }

}
1

There are 1 best solutions below

5
Souhail Chougrani On

without any exernal library you can use this function note that rows (in the class CSVargs) is the data to download so if you pass sorted rows or/and specific length's row should work

 export interface CSVargs<T> { // you can make change T by any
  rows: T[];
  keys: string[];
  titles: string[];
  filename?: string;
  columnDelimiter?: string;
  lineDelimiter?: string;
}
export function downloadCSV(args: CSVargs) {
  args.filename = args.filename || 'export.csv';
  args.columnDelimiter = args.columnDelimiter || ';';
  args.lineDelimiter = args.lineDelimiter || '\n';

  let csv = convertArrayOfObjectsToCSV(args);
  if (csv == null) { return; }

  if (!csv.match(/^data:text\/csv/i)) {
    const bom = '\uFEFF';
    csv = 'data:text/csv;charset=utf-8,' + bom + csv;
  }
  const data = encodeURI(csv);

  const link = document.createElement('a');
  link.setAttribute('href', data);
  link.setAttribute('download', args.filename);
  link.click();
}

function convertArrayOfObjectsToCSV(args: CSVargs) {
  if (!args.rows || !args.rows.length) {
    return;
  }
  if (!args.keys || !args.keys.length) {
    args.keys = Object.keys(args.rows[0]);
  }
  if (!args.titles || !args.titles.length) {
    args.titles = args.keys;
  }
  let result = '';
  result += '"' + args.titles.join('"' + args.columnDelimiter + '"') + '"';
  result += args.lineDelimiter;

  args.rows.forEach(item => {
    let ctr = 0;
    args.keys.forEach(key => {
      if (ctr > 0) {
        result += args.columnDelimiter;
      }
      const value = (item && !item[key]) ? item[key] : ''; // is better to check item not undefined not null not empty (create a function to check is not blanc)
      result += '"' + value + '"';
      ctr++;
    });
    result += args.lineDelimiter;
  });
  return result;
}