synthetic-dom
This commit is contained in:
parent
096b5ddce8
commit
4db5b76501
@ -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" />
|
||||
|
11
docz/static/dom/SheetJSDOM.js
Normal file
11
docz/static/dom/SheetJSDOM.js
Normal file
@ -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");
|
23
docz/static/dom/SheetJSHappyDOM.js
Normal file
23
docz/static/dom/SheetJSHappyDOM.js
Normal file
@ -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");
|
20
docz/static/dom/SheetJSXMLDOM.js
Normal file
20
docz/static/dom/SheetJSXMLDOM.js
Normal file
@ -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> {
|
||||
|
Loading…
Reference in New Issue
Block a user