synthetic-dom

This commit is contained in:
SheetJS 2023-09-11 00:44:15 -04:00
parent 096b5ddce8
commit 4db5b76501
10 changed files with 440 additions and 114 deletions

@ -484,8 +484,14 @@ The data is in the workbook and can be exported.
![Rough export](pathname:///example/rough.png)
There are multiple opportunities for improvement: the headers can be renamed and
the column widths can be adjusted. [SheetJS Pro](https://sheetjs.com/pro) offers
additional styling options like cell styling and frozen rows.
the column widths can be adjusted.
:::tip pass
[SheetJS Pro](https://sheetjs.com/pro) offers additional styling options like
cell styling and frozen rows.
:::
<details><summary><b>Changing Header Names</b> (click to show)</summary>
@ -957,10 +963,17 @@ of the React Native documentation before testing the demo.
:::
:::caution pass
For Android testing, React Native requires Java 11. It will not work with
current Java releases.
:::
Create a new project by running the following commands in the Terminal:
<CodeBlock language="bash">{`\
npx react-native@0.71 init SheetJSPres --version="0.72.0-rc.1"
npx -y react-native@0.72.4 init SheetJSPres --version="0.72.4"
cd SheetJSPres
\n\
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-native-blob-util@0.17.1`}

@ -1266,7 +1266,7 @@ Access `http://localhost:8080` in your web browser and click the export button.
</details>
:::note
:::note pass
The [Vite section of the Content demo](/docs/demos/static/vitejs) covers asset
loaders. They are ideal for static sites pulling data from sheets at build time.

@ -5,10 +5,16 @@ title: Synthetic DOM
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
`table_to_book` / `table_to_sheet` / `sheet_add_dom` act on HTML DOM elements.
Traditionally there is no DOM in server-side environments.
SheetJS offers three methods to directly process HTML DOM TABLE elements[^1]:
:::note
- `table_to_sheet` generates a SheetJS worksheet[^2] from a TABLE element
- `table_to_book` generates a SheetJS workbook[^3] from a TABLE element
- `sheet_add_dom` adds data from a TABLE element to an existing worksheet
These methods work in the web browser. NodeJS and other server-side platforms
traditionally lack a DOM implementation, but third-party modules fill the gap.
:::tip pass
The most robust approach for server-side processing is to automate a headless
web browser. ["Browser Automation"](/docs/demos/net/headless) includes demos.
@ -17,23 +23,60 @@ web browser. ["Browser Automation"](/docs/demos/net/headless) includes demos.
This demo covers synthetic DOM implementations for non-browser platforms.
## Integration Details
SheetJS API methods use DOM features that may not be available.
### Table rows
The `rows` property of TABLE elements is a list of TR row children. This list
automatically updates when rows are added and deleted.
SheetJS does not mutate `rows`. Assuming there are no nested tables, the `rows`
property can be created using `getElementsByTagName`:
```js
tbl.rows = Array.from(tbl.getElementsByTagName("tr"));
```
### Row cells
The `cells` property of TR elements is a list of TD cell children. This list
automatically updates when cells are added and deleted.
SheetJS does not mutate `cells`. Assuming there are no nested tables, the
`cells` property can be created using `getElementsByTagName`:
```js
tbl.rows.forEach(row => row.cells = Array.from(row.getElementsByTagName("td")));
```
## NodeJS
### JSDOM
JSDOM is a DOM implementation for NodeJS. Given an HTML string, a reference to
the table element plays nice with the SheetJS DOM methods:
JSDOM is a DOM implementation for NodeJS. The synthetic DOM elements are
compatible with SheetJS methods.
```js
The following example scrapes the first table from the file `SheetJSTable.html`
and generates a XLSX workbook:
```js title="SheetJSDOM.js"
const XLSX = require("xlsx");
const { readFileSync } = require("fs");
const { JSDOM } = require("jsdom");
/* parse HTML */
const dom = new JSDOM(html_string);
/* obtain HTML string. This example reads from SheetJSTable.html */
const html_str = readFileSync("SheetJSTable.html", "utf8");
// highlight-start
/* get first TABLE element */
const tbl = dom.window.document.querySelector("table");
const doc = new JSDOM(html_str).window.document.querySelector("table");
/* generate workbook */
const workbook = XLSX.utils.table_to_book(tbl);
const workbook = XLSX.utils.table_to_book(doc);
// highlight-end
XLSX.writeFile(workbook, "SheetJSDOM.xlsx");
```
@ -41,7 +84,7 @@ XLSX.writeFile(workbook, "SheetJSDOM.xlsx");
:::note
This demo was last tested on 2023 May 18 against JSDOM `22.0.0`
This demo was last tested on 2023 September 10 against JSDOM `22.1.0`
:::
@ -51,21 +94,7 @@ This demo was last tested on 2023 May 18 against JSDOM `22.0.0`
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz jsdom@22.0.0`}
</CodeBlock>
2) Save the following script to `SheetJSDOM.js`:
```js title="SheetJSDOM.js"
const XLSX = require("xlsx");
const { readFileSync } = require("fs");
const { JSDOM } = require("jsdom");
/* obtain HTML string. This example reads from SheetJSTable.html */
const html_str = readFileSync("SheetJSTable.html", "utf8");
/* get first TABLE element */
const doc = new JSDOM(html_str).window.document.querySelector("table");
/* generate workbook */
const workbook = XLSX.utils.table_to_book(doc);
XLSX.writeFile(workbook, "SheetJSDOM.xlsx");
```
2) Save the previous codeblock to `SheetJSDOM.js`.
3) Download [the sample `SheetJSTable.html`](pathname:///dom/SheetJSTable.html):
@ -83,48 +112,79 @@ The script will create a file `SheetJSDOM.xlsx` that can be opened.
</details>
### XMLDOM
### HappyDOM
XMLDOM provides a DOM framework for NodeJS. Given an HTML string, a reference to
the table element works with the SheetJS DOM methods after patching the object.
HappyDOM provides a DOM framework for NodeJS. For the tested version (`11.0.2`),
the following patches were needed:
- TABLE `rows` property (explained above)
- TR `cells` property (explained above)
<details><summary><b>Complete Demo</b> (click to show)</summary>
:::note
This demo was last tested on 2023 May 18 against XMLDOM `0.8.7`
This demo was last tested on 2023 September 10 against HappyDOM `11.0.2`
:::
1) Install SheetJS and HappyDOM libraries:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz happy-dom@11.0.2`}
</CodeBlock>
2) Download [the sample script `SheetJSHappyDOM.js`](pathname:///dom/SheetJSHappyDOM.js):
```bash
curl -LO https://docs.sheetjs.com/dom/SheetJSHappyDOM.js
```
3) Run the script:
```bash
node SheetJSHappyDOM.js
```
The script will create a file `SheetJSHappyDOM.xlsx` that can be opened.
</details>
### XMLDOM
XMLDOM provides a DOM framework for NodeJS. For the tested version (`0.8.10`),
the following patches were needed:
- TABLE `rows` property (explained above)
- TR `cells` property (explained above)
- Element `innerHTML` property:
```js
Object.defineProperty(tbl.__proto__, "innerHTML", { get: function() {
var outerHTML = new XMLSerializer().serializeToString(this);
if(outerHTML.match(/</g).length == 1) return "";
return outerHTML.slice(0, outerHTML.lastIndexOf("</")).replace(/<[^"'>]*(("[^"]*"|'[^']*')[^"'>]*)*>/, "");
}});
```
<details><summary><b>Complete Demo</b> (click to show)</summary>
:::note
This demo was last tested on 2023 September 10 against XMLDOM `0.8.10`
:::
1) Install SheetJS and XMLDOM libraries:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @xmldom/xmldom@0.8.7`}
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz @xmldom/xmldom@0.8.10`}
</CodeBlock>
2) Save the following codeblock to `SheetJSXMLDOM.js`:
2) Download [the sample script `SheetJSXMLDOM.js`](pathname:///dom/SheetJSXMLDOM.js):
```js title="SheetJSXMLDOM.js"
const XLSX = require("xlsx");
const { DOMParser, XMLSerializer } = require("@xmldom/xmldom");
(async() => {
const text = await (await fetch('https://docs.sheetjs.com/dom/SheetJSTable.html')).text();
const doc = new DOMParser().parseFromString( text, "text/html");
const tbl = doc.getElementsByTagName("table")[0];
/* patch XMLDOM */
tbl.rows = Array.from(tbl.getElementsByTagName("tr"));
tbl.rows.forEach(row => row.cells = Array.from(row.getElementsByTagName("td")))
Object.defineProperty(tbl.__proto__, "innerHTML", { get: function() {
var outerHTML = new XMLSerializer().serializeToString(this);
if(outerHTML.match(/</g).length == 1) return "";
return outerHTML.slice(0, outerHTML.lastIndexOf("</")).replace(/<[^"'>]*(("[^"]*"|'[^']*')[^"'>]*)*>/, "");
}});
const workbook = XLSX.utils.table_to_book(tbl);
XLSX.writeFile(workbook, "SheetJSXMLDOM.xlsx");
})();
```bash
curl -LO https://docs.sheetjs.com/dom/SheetJSXMLDOM.js
```
3) Run the script:
@ -135,28 +195,26 @@ node SheetJSXMLDOM.js
The script will create a file `SheetJSXMLDOM.xlsx` that can be opened.
</details>
### CheerioJS
:::caution
:::caution pass
Cheerio does not support a number of fundamental properties out of the box. They
can be shimmed, but it is strongly recommended to use a more compliant library.
:::
CheerioJS provides a DOM-like framework for NodeJS. Given an HTML string, a
reference to the table element works with the SheetJS DOM methods with some
prototype fixes. [`SheetJSCheerio.js`](pathname:///dom/SheetJSCheerio.js) is a
complete script.
CheerioJS provides a DOM-like framework for NodeJS. Many features were missing.
[`SheetJSCheerio.js`](pathname:///dom/SheetJSCheerio.js) implements the missing
features to ensure that SheetJS DOM methods can process TABLE elements.
<details><summary><b>Complete Demo</b> (click to show)</summary>
:::note
This demo was last tested on 2023 May 18 against Cheerio `1.0.0-rc.12`
This demo was last tested on 2023 September 10 against Cheerio `1.0.0-rc.12`
:::
@ -192,8 +250,11 @@ The script will create a file `SheetJSCheerio.xlsx` that can be opened.
### DenoDOM
DenoDOM provides a DOM framework for Deno. Given an HTML string, a reference to
the table element works with the SheetJS DOM methods after patching the object.
DenoDOM provides a DOM framework for Deno. For the tested version (`0.1.38`),
the following patches were needed:
- TABLE `rows` property (explained above)
- TR `cells` property (explained above)
This example fetches [a sample table](pathname:///dom/SheetJSTable.html):
@ -220,11 +281,11 @@ const workbook = XLSX.utils.table_to_book(tbl);
XLSX.writeFile(workbook, "SheetJSDenoDOM.xlsx");`}
</CodeBlock>
<details open><summary><b>Complete Demo</b> (click to hide)</summary>
<details><summary><b>Complete Demo</b> (click to show)</summary>
:::note
This demo was last tested on 2023 May 18 against DenoDOM `0.1.38`
This demo was last tested on 2023 September 10 against DenoDOM `0.1.38`
:::
@ -238,4 +299,8 @@ deno run --allow-net --allow-write SheetJSDenoDOM.ts
The script will create a file `SheetJSDenoDOM.xlsx` that can be opened.
</details>
</details>
[^1]: See ["HTML Table Input" in "Utilities"](/docs/api/utilities/html#html-table-input)
[^2]: See ["Worksheet Object" in "SheetJS Data Model"](/docs/csf/book) for more details.
[^3]: See ["Workbook Object" in "SheetJS Data Model"](/docs/csf/book) for more details.

@ -1,5 +1,7 @@
---
title: Ionic
title: Data Conduction in Ionic Apps
sidebar_label: Ionic
description: Build data-intensive mobile apps with Ionic and Cordova. Seamlessly integrate spreadsheets into your app using SheetJS. Let data in your Excel spreadsheets shine.
pagination_prev: demos/static/index
pagination_next: demos/desktop/index
sidebar_position: 4
@ -10,10 +12,17 @@ sidebar_custom_props:
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
The [NodeJS Module](/docs/getting-started/installation/nodejs) can be imported
from the main entrypoint or any script in the project.
[Ionic](https://ionicframework.com/) is a mobile app framework for building iOS
and Android apps with the Cordova platform.
The "Complete Example" creates an app that looks like the screenshots below:
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses Ionic and SheetJS to process data and generate spreadsheets.
We'll explore how to load SheetJS in an Ionic app and use Ionic APIs and plugins
to extract data from, and write data to, spreadsheet files on the device.
The ["Demo"](#demo) creates an app that looks like the screenshots below:
<table><thead><tr>
<th><a href="#demo">iOS</a></th>
@ -28,6 +37,13 @@ The "Complete Example" creates an app that looks like the screenshots below:
</td></tr></tbody></table>
:::info pass
This demo covers Ionic apps using the Cordova platform.
The [CapacitorJS demo](/docs/demos/mobile/capacitor) covers CapacitorJS apps.
:::
:::warning Telemetry
@ -57,18 +73,36 @@ npx @capacitor/cli telemetry
## Integration Details
:::caution pass
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
imported from the main entrypoint or any script in the project.
The latest version of Ionic uses CapacitorJS. These notes are for Cordova apps.
The [CapacitorJS demo](/docs/demos/mobile/capacitor) covers CapacitorJS apps.
### Internal State
:::
The ["Angular" demo](/docs/demos/frontend/angular) discusses a number of state
representations and preview strategies.
### Angular
For this demo, the internal state is an "array of arrays"[^1] (`any[][]`):
`Array<Array<any>>` neatly maps to a table with `ngFor`:
```ts title="Array of Arrays state"
import { Component } from '@angular/core';
type AOA = any[][];
```html
@Component({...})
export class SheetJSTablePage {
data: AOA = [
["S", "h", "e", "e", "t", "J", "S"],
[ 5, 4, 3, 3, 7, 9, 5]
];
// ...
}
```
### Displaying Data
`ion-grid`[^2] is a display grid component. The Angular `ngFor` directive[^3]
simplifies iteration over the array of arrays:
```html title="Template for displaying an array of arrays"
<ion-grid>
<ion-row *ngFor="let row of data">
<ion-col *ngFor="let val of row">
@ -78,29 +112,62 @@ The [CapacitorJS demo](/docs/demos/mobile/capacitor) covers CapacitorJS apps.
</ion-grid>
```
### Cordova
### File Operations
`@ionic-native/file` reads and writes files on devices.
`@awesome-cordova-plugins/file` reads and writes files on devices.
_Reading Files_
:::info pass
`readAsArrayBuffer` returns `ArrayBuffer` objects suitable for `array` type:
The plugins in the `@ionic-native` scope have been deprecated. The community
modules in the `@awesome-cordova-plugins` scope should be used.
:::
#### Reading Files
`this.file.readAsArrayBuffer` reads file data from a specified URL and resolves
to `ArrayBuffer` objects.
These objects can be parsed with the SheetJS `read` method[^4]. The SheetJS
`sheet_to_json` method[^5] with the option `header: 1` generates an array of
arrays which can be assigned to the page state:
```ts
/* read a workbook */
/* read a workbook file */
const ab: ArrayBuffer = await this.file.readAsArrayBuffer(url, filename);
/* parse */
const wb: XLSX.WorkBook = XLSX.read(ab, {type: 'array'});
/* generate an array of arrays from the first worksheet */
const ws: XLSX.WorkSheet = wb.SheetNames[wb.Sheets[0]];
const aoa: AOA = XLSX.utils.sheet_to_json(ws, {header: 1});
/* update state */
this.data = aoa;
```
_Writing Files_
#### Writing Files
`array` type can be converted to blobs that can be exported with `writeFile`:
`this.file.writeFile` writes file data stored in `Blob` objects to the device.
From the array of arrays, the SheetJS `aoa_to_sheet` method[^6] generates a
worksheet object. The `book_new` and `book_append_sheet` helpers[^7] generate a
workbook object. The SheetJS `write` method[^8] with the option `type: "array"`
will generate an `ArrayBuffer`, from which a `Blob` can be created:
```ts
/* write a workbook */
const wbout: ArrayBuffer = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
let blob = new Blob([wbout], {type: 'application/octet-stream'});
/* generate worksheet */
const ws: XLSX.WorkSheet = XLSX.utils.aoa_to_sheet(this.data);
/* generate workbook and add the worksheet */
const wb: XLSX.WorkBook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'SheetJS');
/* write XLSX to ArrayBuffer */
const ab: ArrayBuffer = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
/* generate Blob */
let blob = new Blob([ab], {type: 'application/octet-stream'});
/* write Blob to device */
this.file.writeFile(url, filename, blob, {replace: true});
```
@ -108,47 +175,72 @@ this.file.writeFile(url, filename, blob, {replace: true});
:::note
This demo was tested on an Intel Mac on 2023 March 28 with Cordova.
The file integration uses `@ionic-native/file` version `5.36.0`.
The project was last tested in 2023 September 10. `ionic info` showed:
The iOS simulator runs iOS 15.5 on an iPhone SE (3rd Generation).
- Ionic: `@ionic/angular 7.3.3`, `@ionic/angular-toolkit 9.0.0`
- Cordova: `cordova-lib@12.0.1`, `android 12.0.1, ios 7.0.1`
The Android simulator runs Android 12.0 (S) API 31 on a Pixel 3.
The file integration uses `@awesome-cordova-plugins/file` version `6.4.0`.
The iOS demo was last tested on 2023 September 10 on an emulated iPhone SE
(3rd generation) + iOS 16.4
The Android demo was last tested on 2023 September 10 on an emulated Pixel 3 +
Android 13 ("Tiramisu") API 33.
:::
The app in this demo will display data in a table.
On load, a [test file](https://sheetjs.com/pres.numbers) will be processed.
When a document is selected with the file picker, it will be processed and the
table will refresh to show the contents.
"Import Data" will attempt to read `SheetJSIonic.xlsx` from a known location. An
alert will display the expected location.
"Export Data" will attempt to export the table data to `SheetJSIonic.xlsx` in a
known location. After writing, an alert will display the location of the file.
### Platform Setup
0) Disable telemetry as noted in the warning.
Install required global dependencies:
1) Follow the official instructions for iOS and Android development[^9].
2) Install required global dependencies:
```bash
npm i -g cordova-res @angular/cli native-run @ionic/cli
```
Follow the [React Native demo](/docs/demos/mobile/reactnative) to ensure iOS and Android sims are ready.
### Base Project
1) Create a new project:
3) Create a new project:
```bash
ionic start SheetJSIonic blank --type angular --cordova --quiet --no-git --no-link --confirm
```
When asked to select `NgModules` or `Standalone Components`, select `NgModules`
If a prompt asks to confirm Cordova use, enter `Yes` to continue.
If a prompt asks about creating an Ionic account, enter `N` to opt out.
2) Set up Cordova:
4) Set up Cordova:
```bash
cd SheetJSIonic
ionic cordova plugin add cordova-plugin-file
ionic cordova platform add ios --confirm
ionic cordova platform add android --confirm
npm i --save @ionic-native/core @ionic-native/file @ionic/cordova-builders
npm i --save @awesome-cordova-plugins/core @awesome-cordova-plugins/file @ionic/cordova-builders
```
:::note
:::note pass
If `cordova-plugin-file` is added before the platforms, installation may fail:
@ -165,9 +257,9 @@ ionic cordova platform add ios --confirm
:::
:::caution
:::caution pass
If the `npm i` fails due to `rxjs` resolution, add the highlighted lines
If the `npm i` step fails due to `rxjs` resolution, add the highlighted lines
to `package.json` to force a resolution:
```js title="package.json"
@ -180,24 +272,26 @@ to `package.json` to force a resolution:
"dependencies": {
```
Note that the required `rxjs` version will be displayed in the error log.
After adding the lines, the `npm i` command will succeed.
:::
3) Install dependencies:
5) Install dependencies:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</CodeBlock>
4) Add `@ionic-native/file` to the module. Differences highlighted below:
6) Add `@awesome-cordova-plugins/file` to the module. Differences highlighted below:
```ts title="src/app/app.module.ts"
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
// highlight-next-line
import { File } from '@ionic-native/file/ngx';
import { File } from '@awesome-cordova-plugins/file/ngx';
@NgModule({
declarations: [AppComponent],
@ -210,19 +304,41 @@ import { File } from '@ionic-native/file/ngx';
export class AppModule {}
```
5) Download [`home.page.ts`](pathname:///ionic/home.page.ts) and replace:
7) Download [`home.page.ts`](pathname:///ionic/home.page.ts) and replace:
```bash
curl -o src/app/home/home.page.ts -L https://docs.sheetjs.com/ionic/home.page.ts
```
**iOS Testing**
### iOS
8) Enable file sharing and make the documents folder visible in the iOS app.
Add the following lines to `platforms/ios/SheetJSIonic/SheetJSIonic-Info.plist`:
```xml title="platforms/ios/SheetJSIonic/SheetJSIonic-Info.plist (add to file)"
<plist version="1.0">
<dict>
<!-- highlight-start -->
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<!-- highlight-end -->
<key>CFBundleDevelopmentRegion</key>
```
(The root element of the document is `plist` and it contains one `dict` child)
9) Build the app and start the simulator
```bash
ionic cordova emulate ios
```
:::caution
When the app is loaded, a list of Presidents should be displayed. This list is
dynamically generated by fetching and parsing a test file.
:::caution pass
In some test runs, the `cordova build ios --emulator` step failed with error:
@ -239,13 +355,54 @@ npm i --save cordova-ios
:::
**Android Testing**
:::info pass
In the most recent test, the `native-run ios` command failed with
```
[native-run] ERR_UNKNOWN: Path 'platforms/ios/build/emulator/SheetJSIonic.app' not found
```
Inspecting `platforms/ios/build/`, the actual folder name was:
```bash
% ls platforms/ios/build
#highlight-next-line
Debug-iphonesimulator
```
The iOS simulator can be launched manually:
```bash
native-run ios --app platforms/ios/build/Debug-iphonesimulator/SheetJSIonic.app --virtual
```
:::
### Android
10) Enable file reading and writing in the Android app.
Edit `platforms/android/app/src/main/AndroidManifest.xml` and add the following
two lines before the `application` tag:
```xml title="platforms/android/app/src/main/AndroidManifest.xml (add to file)"
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
```
In the `application` tag, add the attribute `android:requestLegacyExternalStorage="true"`.
11) Build the app and start the emulator
```bash
ionic cordova emulate android
```
:::caution
When the app is loaded, a list of Presidents should be displayed. This list is
dynamically generated by fetching and parsing a test file.
:::caution pass
In some test runs, `cordova build android --emulator` step failed with error:
@ -260,3 +417,40 @@ npm i --save cordova-android
```
:::
:::caution pass
In some tests, the build failed with a Gradle error:
```
Could not find an installed version of Gradle either in Android Studio,
or on your system to install the gradle wrapper. Please include gradle
in your path or install Android Studio
```
On macOS, this issue was resolved by installing gradle with Homebrew manager:
```bash
brew install gradle
```
:::
:::warning pass
When the demo was last tested on Android, reading files worked as expected.
However, the generated files were not externally visible from the Files app.
**This is a known bug with Android SDK 33 and the underlying file plugins!**
:::
[^1]: See ["Array of Arrays" in the API reference](/docs/api/utilities/array#array-of-arrays)
[^2]: See [`ion-grid`](https://ionicframework.com/docs/api/grid) in the Ionic documentation.
[^3]: See [`ngFor`](https://angular.io/api/common/NgFor) in the Angular documentation.
[^4]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^5]: See ["Array Output" in "Utility Functions"](/docs/api/utilities/array#array-output)
[^6]: See [`aoa_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
[^7]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
[^8]: See [`write` in "Writing Files"](/docs/api/write-options)
[^9]: See ["Developing for iOS"](https://ionicframework.com/docs/v6/developing/ios) and ["Developing for Android"](https://ionicframework.com/docs/v6/developing/android) in the v6 Ionic framework documentation.

@ -200,7 +200,7 @@ after the `Permissions` comment:
<!-- highlight-start -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- highlight-end -->
<uses-permission android:name="android.permission.INTERNET" />

@ -0,0 +1,11 @@
const XLSX = require("xlsx");
const { readFileSync } = require("fs");
const { JSDOM } = require("jsdom");
/* obtain HTML string. This example reads from SheetJSTable.html */
const html_str = readFileSync("SheetJSTable.html", "utf8");
/* get first TABLE element */
const doc = new JSDOM(html_str).window.document.querySelector("table");
/* generate workbook */
const workbook = XLSX.utils.table_to_book(doc);
XLSX.writeFile(workbook, "SheetJSDOM.xlsx");

@ -0,0 +1,23 @@
const XLSX = require("xlsx");
const { readFileSync } = require("fs");
const { Window } = require("happy-dom");
/* obtain HTML string. This example reads from SheetJSTable.html */
const html_str = readFileSync("SheetJSTable.html", "utf8");
/* get table element */
const window = new Window({
url: "https://localhost:8080",
width: 1024,
height: 768
});
window.document.body.innerHTML = html_str;
const tbl = window.document.body.getElementsByTagName("table")[0];
/* add `rows` and `cells` properties */
tbl.rows = Array.from(tbl.getElementsByTagName("tr"));
tbl.rows.forEach(row => row.cells = Array.from(row.getElementsByTagName("td")))
/* generate workbook */
const workbook = XLSX.utils.table_to_book(tbl);
XLSX.writeFile(workbook, "SheetJSHappyDOM.xlsx");

@ -0,0 +1,20 @@
const XLSX = require("xlsx");
const { DOMParser, XMLSerializer } = require("@xmldom/xmldom");
(async() => {
const text = await (await fetch('https://docs.sheetjs.com/dom/SheetJSTable.html')).text();
const doc = new DOMParser().parseFromString( text, "text/html");
const tbl = doc.getElementsByTagName("table")[0];
/* patch XMLDOM */
tbl.rows = Array.from(tbl.getElementsByTagName("tr"));
tbl.rows.forEach(row => row.cells = Array.from(row.getElementsByTagName("td")));
Object.defineProperty(tbl.__proto__, "innerHTML", { get: function() {
var outerHTML = new XMLSerializer().serializeToString(this);
if(outerHTML.match(/</g).length == 1) return "";
return outerHTML.slice(0, outerHTML.lastIndexOf("</")).replace(/<[^"'>]*(("[^"]*"|'[^']*')[^"'>]*)*>/, "");
}});
const workbook = XLSX.utils.table_to_book(tbl);
XLSX.writeFile(workbook, "SheetJSXMLDOM.xlsx");
})();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 46 KiB

@ -1,7 +1,7 @@
/* sheetjs (C) 2013-present SheetJS -- https://sheetjs.com */
/* vim: set ts=2: */
import { Component, OnInit } from '@angular/core';
import { File } from '@ionic-native/file/ngx';
import { File } from '@awesome-cordova-plugins/file/ngx';
import * as XLSX from 'xlsx';
type AOA = any[][];
@ -42,7 +42,7 @@ type AOA = any[][];
})
export class HomePage implements OnInit {
data: any[][] = [[1,2,3],[4,5,6]];
data: AOA = [[1,2,3],[4,5,6]];
constructor(public file: File) {}
async ngOnInit(): Promise<void> {