{"id":932,"date":"2023-01-04T10:35:43","date_gmt":"2023-01-04T15:05:43","guid":{"rendered":"http:\/\/gregorgonzalez.com.ve\/blog\/?p=932"},"modified":"2023-01-04T10:49:11","modified_gmt":"2023-01-04T15:19:11","slug":"sheetjs-agregar-datos-a-archivo-de-excel-existente-desde-angular","status":"publish","type":"post","link":"https:\/\/gregorgonzalez.com.ve\/blog\/sheetjs-agregar-datos-a-archivo-de-excel-existente-desde-angular\/","title":{"rendered":"SheetJs agregar datos a archivo de excel existente desde Angular"},"content":{"rendered":"<p>Al trabajar con datos, registros, listas, muchas veces nos solicitan poder descargarlos en Excel, la librer\u00eda <a href=\"https:\/\/sheetjs.com\/\" target=\"_blank\" rel=\"noopener\">https:\/\/sheetjs.com\/<\/a> nos permite crear y manipular archivos de Excel desde el navegador o un servidor, es decir se puede usar tanto en proyectos Angular como en Node.<\/p>\n<p>Su instalaci\u00f3n es sencilla con NPM<\/p>\n<pre class=\"lang:default decode:true \">npm i --save https:\/\/cdn.sheetjs.com\/xlsx-0.19.1\/xlsx-0.19.1.tgz<\/pre>\n<p>Tambi\u00e9n hay otras formas de instalar en la p\u00e1gina oficial:<br \/>\n<a href=\"https:\/\/docs.sheetjs.com\/docs\/getting-started\/installation\/frameworks\">https:\/\/docs.sheetjs.com\/docs\/getting-started\/installation\/frameworks<\/a><\/p>\n<p>Luego podemos importarlo en nuestro proyecto:<\/p>\n<pre class=\"lang:ts decode:true \">import * as XLSX from 'xlsx';<\/pre>\n<p>O si solo lo necesitas en un m\u00e9todo en espec\u00edfico, cargarlo en una variable:<\/p>\n<pre class=\"lang:default decode:true\">const XLSX = require('xlsx');<\/pre>\n<p>&nbsp;<\/p>\n<p>Esta librer\u00eda nos permite convertir datos, array, objetos json a una hoja de Excel listos para convertir en archivo y descargarlo. As\u00ed como tambi\u00e9n nos permite leer archivos en Excel y poder manipularlos.<\/p>\n<p>Lo que se complica es la parte de lectura desde archivos locales del proyecto, supongamos que tengas una plantilla con t\u00edtulos y solo quieras \u201cllenarla\u201d con los datos.<\/p>\n<p>Ejemplo que tengas una ruta \u00abassets\/misplantillas\/reporte.xlsx\u00bb<\/p>\n<p>Primero el navegador no permite acceder a rutas locales, incluso aparecer\u00e1 un error \u00ab_fs.readFileSync is not a function\u00bb, donde los m\u00e9todos para acceder archivos no pueden realizar la acci\u00f3n. En node no habr\u00eda problemas porque se ejecutar\u00eda desde el lado del servidor.<\/p>\n<p>Segundo para estos casos la p\u00e1gina oficial recomienda es cargarlo por petici\u00f3n HTTP, acceder por la ruta URL como si fuese un elemento externo y luego leer el archivo en binario para luego pasarlo al lector de archivos de Excel. Nos obliga a realizar m\u00e1s pasos, pero no hay otra alternativa.<\/p>\n<p>&nbsp;<\/p>\n<p>Ejemplo comentando del proceso, un servicio \u00abExcelDownloadService\u00bb:<\/p>\n<pre class=\"lang:ts decode:true\">import { HttpClient } from '@angular\/common\/http';\r\nimport { Injectable } from '@angular\/core';\r\nimport * as FileSaver from 'file-saver';\r\n\r\n@Injectable({\r\n  providedIn: 'root'\r\n})\r\nexport class ExcelDownloadService {\r\n  private httpClient: HttpClient;\r\n\r\n  constructor(\r\n    http: HttpClient\r\n  ) {\r\n    this.httpClient = http;\r\n  }\r\n\r\n  \/\/ dataJson es un array de objetos \"array[]={ columna: dato }\"\r\n  createExcel(dataJson: any, nameFile: string) {\r\n    \/\/ Importamos la libreria manualmente\r\n    import(\"xlsx\").then(xlsx =&gt; {\r\n      const templateFile = \"assets\/misplantillas\/reporte.xlsx\";\r\n      \r\n      \/\/ Buscamos el archivo excel desde una petici\u00f3n http\r\n      this.httpClient.get(templateFile, { responseType: 'blob' })\r\n        .subscribe((data: any) =&gt; {\r\n          const reader: FileReader = new FileReader();\r\n\r\n          \/\/ Al obtener el archivo \"data\", debemos leerlo con FileReader\r\n          \/\/ asociamos una funci\u00f3n al \"onload\" para ejecutarla cuando cargue\r\n          reader.onload = (e: any) =&gt; { \r\n            const bstr: string = e.target.result;\r\n            \r\n            const wb = xlsx.read(bstr, {type: 'binary'}); \/\/ Le indicamos a la librer\u00eda que lea el archivo como binario\r\n            const firstSheetName = wb.SheetNames[0];  \/\/ Obtenemos el nombre de la primera pesta\u00f1a            \r\n            const sheet = wb.Sheets[firstSheetName]; \/\/ Obtenemos la pesta\u00f1a seg\u00fan el nombre\r\n           \r\n            \/\/ A la pesta\u00f1a le agregamos los datos nuevos dataJson, salt\u00e1ndonos el encabezado y empezando en la fila 3\r\n            xlsx.utils.sheet_add_json(sheet, dataJson, { skipHeader:true, origin: \"A3\" });\r\n\r\n            \/\/ Creamos el archivo nuevo y lo mandamos a descargar por el navegador\r\n            const excelBuffer: any = xlsx.write(wb, { bookType: 'xlsx', type: 'array' });\r\n            this.saveFile(excelBuffer, nameFile);\r\n          };\r\n\r\n          \/\/ Arriba se asocia la funci\u00f3n al \"onload\", aqu\u00ed ejecutamos el reader que luego ejecuta el \"onload\"\r\n          reader.readAsBinaryString(data);\r\n        });\r\n    });\r\n  }\r\n\r\n  \/\/ Recibe el Excel y lo genera para descargar desde el navegador\r\n  private saveFile(buffer: any, fileName: string): void {\r\n    const data: Blob = new Blob([buffer], {\r\n      type: \"application\/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8\"\r\n    });\r\n    FileSaver.saveAs(data, fileName + \".xksx\");\r\n  }\r\n}\r\n<\/pre>\n<p>En las opciones coloqu\u00e9 \u00ab<span class=\"crayon-sy\">{<\/span> <span class=\"crayon-v\">skipHeader<\/span><span class=\"crayon-o\">:<\/span><span class=\"crayon-t\">true<\/span><span class=\"crayon-sy\">,<\/span> <span class=\"crayon-v\">origin<\/span><span class=\"crayon-o\">:<\/span> <span class=\"crayon-s\">\u00abA3\u00bb<\/span> <span class=\"crayon-sy\">}<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">;<\/span>\u00bb porque autom\u00e1ticamente se toma los atributos del Json como columnas t\u00edtulo, para ello el skipHeader y origin que comenzara en Fila A3 porqu\u00e9 en mi plantilla tengo dos t\u00edtulos:<\/p>\n<p>[ Reporte Principal ]<br \/>\n[Secci\u00f3n 1][ Secci\u00f3n 2 ][ Secci\u00f3n 3 ]<br \/>\n[datos][datos][datos][datos][datos]<\/p>\n<p>As\u00ed que debes modificar las opciones seg\u00fan lo que requieras.<\/p>\n<p>Tambi\u00e9n he visto que se puede usar con archivos Excel cargados en un formulario, donde el archivo se obtiene directamente y no tendr\u00edas que leer el archivo local pero eso ya son otros casos y otros procesos.<\/p>\n<p><strong>Nota:<\/strong> Esta librer\u00eda es la versi\u00f3n comunity y no permite el uso de estilos \u201cnegritas\u201d, \u201ccentrar celdas\u201d, etc. Incluso si el template tiene estilos, se borrar\u00edan. Para poder usar estilos te piden que compres la versi\u00f3n PRO, tambi\u00e9n podr\u00edas usar alguna de las alternativas basadas en Forks donde le incluyen una parte de estilos.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Al trabajar con datos, registros, listas, muchas veces nos solicitan poder descargarlos en Excel, la librer\u00eda https:\/\/sheetjs.com\/ nos permite crear y manipular archivos de Excel desde el navegador o un servidor, es decir se puede usar tanto en proyectos Angular como en Node. Su instalaci\u00f3n es sencilla con NPM npm i &#8211;save https:\/\/cdn.sheetjs.com\/xlsx-0.19.1\/xlsx-0.19.1.tgz Tambi\u00e9n hay [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":933,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_exactmetrics_skip_tracking":false,"_exactmetrics_sitenote_active":false,"_exactmetrics_sitenote_note":"","_exactmetrics_sitenote_category":0,"footnotes":""},"categories":[232],"tags":[349,350,352,353,351],"_links":{"self":[{"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/posts\/932"}],"collection":[{"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/comments?post=932"}],"version-history":[{"count":4,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/posts\/932\/revisions"}],"predecessor-version":[{"id":937,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/posts\/932\/revisions\/937"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/media\/933"}],"wp:attachment":[{"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/media?parent=932"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/categories?post=932"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/gregorgonzalez.com.ve\/blog\/wp-json\/wp\/v2\/tags?post=932"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}