DEV Community

Juan Eneque
Juan Eneque

Posted on

Angular +SheetJS: 3 formas para crear un excel

En este tutorial les mostrare 3 formas para generar un Excel con la ayuda de la librería SheetJS. También haré uso de la Pokemon API para trabajar con datos reales.

Paso 1: Crear proyecto angular e instalar dependencia

Abrimos una consola CMD y ejecutamos el siguiente comando:

ng new simple-excel-report
Enter fullscreen mode Exit fullscreen mode

Nos ubicamos en la carpeta del proyecto con el comando cd e instalamos la librería SheetJS ejecutando el siguiente comando:

npm i xlsx
Enter fullscreen mode Exit fullscreen mode

Paso 2: Integrando Pokemon API

Para este caso solo haré uso del siguiente endpoint:

https://pokeapi.co/api/v2/pokemon/?limit=5&offset=0
Enter fullscreen mode Exit fullscreen mode

Módulo para servicios compartidos

Crearemos nuestro SharedModule y en el servicio para consultar el API. Ejecutamos los siguientes comandos en la consola:

ng g module shared

ng g service shared/services/pokemon
Enter fullscreen mode Exit fullscreen mode

Importar módulo HTTP

Ahora, en el archivo shared.module.ts importamos el HttpClientModule para poder realizar peticiones http:

/* shared.module.ts */
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http'

@NgModule({
  declarations: [],
  imports: [
    CommonModule,
    HttpClientModule
  ]
})
export class SharedModule { }

Enter fullscreen mode Exit fullscreen mode

Interfaces

Dentro de la carpeta shared, crearemos una subcarpeta llamada interfaces y dentro de ella, el archivo pokemon-response.interface.ts, el cual tendrá el siguiente contenido:

/* pokemon-response.interface.ts */
export interface IPokemonResponse {
  count: number;
  next: string;
  previous: string;
  results: IPokemonInfo[]
}

export interface IPokemonInfo {
  name: string;
  url: string;
}

Enter fullscreen mode Exit fullscreen mode

Crear consulta http

Abrimos el archivo pokemon.service.ts y en el constructor inyectamos el HttpClient de la siguiente manera:

constructor(private network: HttpClient) { }
Enter fullscreen mode Exit fullscreen mode

Luego, creamos nuestro método getPokemonList para hacer la petición GET que traerá la lista de pokemones:

readonly API_BASE = 'https://pokeapi.co/api/v2';

getPokemonList(offset: number, limit: number): Observable<IPokemonResponse> {
    const endpoint = `${this.API_BASE}/pokemon/?offset=${offset}&limit=${limit}`;
    return this.network.get<IPokemonResponse>(endpoint);
  }
Enter fullscreen mode Exit fullscreen mode

Como podrán notar, agregamos 2 parámetros (offset y limit) en el método:

  • offset: ID del pokemon inicial de la lista de respuesta
  • limit: cantidad de pokemones subsiguientes al offset.

Paso 3: Servicio para generar reporte

Desde la consola, ejecutamos el siguiente comando para generar el servicio:

ng g service shared/services/excel-report
Enter fullscreen mode Exit fullscreen mode

Forma #1: Configuración por default

Abrimos el archivo excel-report.service.ts y definimos el método generateDefaultReport:

generateDefaultReport(data: IPokemonInfo[], filename: string) {
    let workbook = XLSX.utils.book_new();
    let worksheet = XLSX.utils.json_to_sheet(data);

    // first way to add a sheet
    workbook.SheetNames.push('Hoja 1');
    workbook.Sheets['Hoja 1'] = worksheet;

    // second way to add a sheet
    // XLSX.utils.book_append_sheet(workbook, worksheet, "Hoja 1")

    XLSX.writeFileXLSX(workbook, filename, {});
  }
Enter fullscreen mode Exit fullscreen mode

Este método recibe 2 parámetros:

  • data: Arreglo de objectos de tipo IPokemonInfo.
  • filename: Nombre del archivo (Ej: mi_reporte.xlsx).

El problema con esta forma es que los headers (fila A1 del Excel) son asignados automáticamente y estos serán igual al nombre de los atributos del objeto. En otras palabras, nuestros headers serán name y url (atributos de IPokemonInfo).

Forma #2: Usando diccionario

Primero crearemos una interface con el nombre custom-header.interface.ts y con la siguiente estructura:

export interface ICustomHeader {
  name: string;
  key: string;
}
Enter fullscreen mode Exit fullscreen mode

Esta interface nos ayudará a construir un diccionario relación, donde name es el nombre de la columna y key es el nombre del atributo de la interface IPokemonInfo. Ej:

  • name: "Info URL"
  • key: url Teniendo esta relación definida, podemos generar un excel con headers personalizados.

A continuación, abrimos el archivo excel-report.service.ts y definimos el método generateReportWithDict:

generateReportWithDict(customHeaders: ICustomHeader[], data: IPokemonInfo[], filename: string) {
    let worksheetData: any[] = [];

    Object(data).forEach( (item: any) => {
      let worksheetItem = Object();
      customHeaders.forEach( header => {
        worksheetItem[header.name] = item[header.key];
      })
      worksheetData.push(worksheetItem)
    })

    // excel file
    let workbook = XLSX.utils.book_new();
    let worksheet = XLSX.utils.json_to_sheet(worksheetData);
    XLSX.utils.book_append_sheet(workbook, worksheet, "Hoja 1")
    XLSX.writeFileXLSX(workbook, filename, {});
  }
Enter fullscreen mode Exit fullscreen mode

Este método recibe 3 parámetros:

  • customHeaders: Arreglo de objectos ICustomHeader.
  • data: Arreglo de objectos de tipo IPokemonInfo.
  • filename: Nombre del archivo (Ej: mi_reporte.xlsx).

Una de las ventajas de trabajar de esta forma es que nosotros podemos definir el orden de las columnas gracias a la relación name-key.

Forma #3: Usando un adapter

Creamos una carpeta adapter y dentro de ella, añadimos el archivo pokemon-report.class.ts con el siguiente contenido:

import { IPokemonInfo } from "../interfaces/pokemon-response.interface";

export interface IPokemonReport {
  pokemonId: number;
  pokemonName: string;
  pokemonUrlInfo: string;
}


export class PokemonReportAdapter {
  data: IPokemonReport[] = [];

  constructor(pokemonList: IPokemonInfo[]) {
    pokemonList.forEach( (pokemon, index) => {
      const reportItem: IPokemonReport = {
        pokemonId: index + 1,
        pokemonName: pokemon.name,
        pokemonUrlInfo: pokemon.url
      }
      this.data.push(reportItem);
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

Una ventaja de usar un adapter, es que podemos realizar operaciones y/o procesar los datos de nuestro objeto para posteriormente asignarlos en un nuevo modelo. En este caso, definimos una interface IPokemonReport que usaremos para generar un nuevo arreglo de datos a partir de nuestra data (IPokemonInfo).

Finalmente, abrimos el archivo excel-report.service.ts y definimos el método generateReportWithAdapter:

generateReportWithAdapter(headers: string[], data: IPokemonReport[], filename: string) {
    let workbook = XLSX.utils.book_new();
    let worksheet = XLSX.utils.json_to_sheet([], { header: headers });

    XLSX.utils.sheet_add_json(worksheet, data, { origin: 'A2', skipHeader: true })

    XLSX.utils.book_append_sheet(workbook, worksheet, "Hoja 1")
    XLSX.writeFileXLSX(workbook, filename);
  }
Enter fullscreen mode Exit fullscreen mode

Este método recibe 3 parámetros:

  • headers: Arreglo de string, donde cada item representa el header de cada columna.
  • data: Arreglo de objectos de tipo IPokemonInfo
  • filename: Nombre del archivo (Ej: mi_reporte.xlsx)

Una consideración importante respecto a esta forma es el orden en que se definen el nombre de los header ya que tienen que tener el mismo orden en que se definen los atributos de la nueva interface (IPokemonReport).


Espero les haya sido de utilidad este tutorial 👍.

Link del repositorio: simple-excel-report

Top comments (0)