ng17
This commit is contained in:
parent
a4ad3195f5
commit
8f9512b217
@ -1,5 +1,7 @@
|
||||
---
|
||||
title: Angular
|
||||
title: Sheets in Angular Sites
|
||||
sidebar_label: Angular
|
||||
description: Build interactive websites with Angular. Seamlessly integrate spreadsheets into your app using SheetJS. Bring Excel-powered workflows and data to the modern web.
|
||||
pagination_prev: demos/index
|
||||
pagination_next: demos/grid/index
|
||||
sidebar_position: 3
|
||||
@ -10,18 +12,24 @@ import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
Angular is a JS library for building user interfaces.
|
||||
Angular is a JS library for building user interfaces.[^1]
|
||||
|
||||
This demo tries to cover common Angular data flow ideas and strategies. Angular
|
||||
and TypeScript familiarity is assumed.
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
**SheetJS plays nice with each version of Angular**.
|
||||
This demo uses Angular and SheetJS to process and generate spreadsheets. We'll
|
||||
explore how to load SheetJS in Angular projects and compare common state models
|
||||
and data flow strategies.
|
||||
|
||||
Other demos cover general Angular deployments, including:
|
||||
:::note pass
|
||||
|
||||
This demo focuses on Angular concepts. Other demos cover general deployments:
|
||||
|
||||
- [iOS and Android applications powered by NativeScript](/docs/demos/mobile/nativescript)
|
||||
- [iOS and Android applications powered by Ionic](/docs/demos/mobile/ionic)
|
||||
|
||||
:::
|
||||
|
||||
:::caution pass
|
||||
|
||||
Angular tooling uses native NodeJS modules. There are a number of issues when
|
||||
@ -61,31 +69,33 @@ import { read, utils, writeFile } from 'xlsx';
|
||||
The various SheetJS APIs work with various data shapes. The preferred state
|
||||
depends on the application.
|
||||
|
||||
:::warning pass
|
||||
|
||||
Angular 17 broke backwards compatibility with projects using Angular 2 - 16.
|
||||
|
||||
**Despite the Angular turmoil, SheetJS plays nice with each version of Angular**.
|
||||
|
||||
When relevant, code snippets for Angular 17 and Angular 2 - 16 are included. The
|
||||
"Angular 2-16" and "Angular 17+" tabs change the displayed code blocks
|
||||
|
||||
:::
|
||||
|
||||
### Array of Objects
|
||||
|
||||
Typically, some users will create a spreadsheet with source data that should be
|
||||
loaded into the site. This sheet will have known columns. For example, our
|
||||
[presidents sheet](https://sheetjs.com/pres.xlsx) has "Name" / "Index" columns:
|
||||
loaded into the site. This sheet will have known columns.
|
||||
|
||||
#### State
|
||||
|
||||
The example [presidents sheet](https://sheetjs.com/pres.xlsx) has one header row
|
||||
with "Name" and "Index" columns. The natural JS representation is an object for
|
||||
each row, using the values in the first rows as keys:
|
||||
|
||||
<table><thead><tr><th>Spreadsheet</th><th>State</th></tr></thead><tbody><tr><td>
|
||||
|
||||
![`pres.xlsx` data](pathname:///pres.png)
|
||||
|
||||
This naturally maps to an array of typed objects, as in the TS example below:
|
||||
|
||||
```ts
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
interface President {
|
||||
Name: string;
|
||||
Index: number;
|
||||
}
|
||||
|
||||
const f = await (await fetch("https://sheetjs.com/pres.xlsx")).arrayBuffer();
|
||||
const wb = read(f);
|
||||
const data = utils.sheet_to_json<President>(wb.Sheets[wb.SheetNames[0]]);
|
||||
console.log(data);
|
||||
```
|
||||
|
||||
`data` will be an array of objects:
|
||||
</td><td>
|
||||
|
||||
```js
|
||||
[
|
||||
@ -97,8 +107,199 @@ console.log(data);
|
||||
]
|
||||
```
|
||||
|
||||
A component will typically loop over the data using `*ngFor`. The following
|
||||
example generates a TABLE with a row for each President:
|
||||
</td></tr></tbody></table>
|
||||
|
||||
This data is typically stored as an array of objects in the component class:
|
||||
|
||||
```ts
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({ /* ... component configuration options ... */ })
|
||||
export class AppComponent {
|
||||
/* the component state is an array of objects */
|
||||
// highlight-next-line
|
||||
rows: any[] = [ { Name: "SheetJS", Index: 0 }];
|
||||
}
|
||||
```
|
||||
|
||||
When the spreadsheet header row is known ahead of time, row typing is possible:
|
||||
|
||||
```ts
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
interface President {
|
||||
Name: string;
|
||||
Index: number;
|
||||
}
|
||||
|
||||
@Component({ /* ... component configuration options ... */ })
|
||||
export class AppComponent {
|
||||
/* the component state is an array of presidents */
|
||||
// highlight-next-line
|
||||
rows: President[] = [ { Name: "SheetJS", Index: 0 }];
|
||||
}
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
|
||||
The types are informative. They do not enforce that worksheets include the named
|
||||
columns. A runtime data validation library should be used to verify the dataset.
|
||||
|
||||
When the file header is not known in advance, `any` should be used.
|
||||
|
||||
:::
|
||||
|
||||
#### Updating State
|
||||
|
||||
The SheetJS [`read`](/docs/api/parse-options) and [`sheet_to_json`](/docs/api/utilities/array#array-output)
|
||||
functions simplify state updates. They are best used in the function bodies of
|
||||
`ngOnInit`[^2] and event handlers.
|
||||
|
||||
A `ngOnInit` method can download and update state when a person loads the site:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
url[(Remote\nFile)]
|
||||
ab[(Data\nArrayBuffer)]
|
||||
wb(SheetJS\nWorkbook)
|
||||
ws(SheetJS\nWorksheet)
|
||||
aoo(array of\nobjects)
|
||||
state((component\nstate))
|
||||
url --> |fetch\n\n| ab
|
||||
ab --> |read\n\n| wb
|
||||
wb --> |wb.Sheets\nselect sheet| ws
|
||||
ws --> |sheet_to_json\n\n| aoo
|
||||
aoo --> |setPres\nfrom `setState`| state
|
||||
```
|
||||
|
||||
```ts
|
||||
import { Component } from '@angular/core';
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
interface President { Name: string; Index: number };
|
||||
|
||||
@Component({ /* ... component configuration options ... */ })
|
||||
export class AppComponent {
|
||||
rows: President[] = [ { Name: "SheetJS", Index: 0 }];
|
||||
ngOnInit(): void { (async() => {
|
||||
/* Download from https://sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
// highlight-start
|
||||
/* parse workbook */
|
||||
const wb = read(ab);
|
||||
|
||||
/* generate array of objects from first worksheet */
|
||||
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
|
||||
const data = utils.sheet_to_json<President>(ws); // generate objects
|
||||
|
||||
/* update data */
|
||||
this.rows = data;
|
||||
// highlight-end
|
||||
})(); }
|
||||
}
|
||||
```
|
||||
|
||||
#### Rendering Data
|
||||
|
||||
Components typically render HTML tables from arrays of objects. The `<tr>` table
|
||||
row elements are typically generated by mapping over the state array, as shown
|
||||
in the example template.
|
||||
|
||||
:::caution pass
|
||||
|
||||
Angular 2 - 16 recommended using `ngFor`[^3]. Angular 17 no longer supports the
|
||||
storied syntax, instead opting for a `@for` block reminiscent of JavaScript[^4].
|
||||
|
||||
:::
|
||||
|
||||
<Tabs groupId="ngVer">
|
||||
<TabItem value="2" label="Angular 2-16">
|
||||
|
||||
```html title="Example Template for displaying arrays of objects (Angular 2-16)"
|
||||
<div class="content" role="main"><table>
|
||||
<thead><th>Name</th><th>Index</th></thead>
|
||||
<tbody>
|
||||
// highlight-start
|
||||
<tr *ngFor="let row of rows">
|
||||
<td>{{row.Name}}</td>
|
||||
<td>{{row.Index}}</td>
|
||||
</tr>
|
||||
// highlight-end
|
||||
</tbody>
|
||||
</table></div>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="17" label="Angular 17+">
|
||||
|
||||
```html title="Example Template for displaying arrays of objects (Angular 17+)"
|
||||
<div class="content" role="main"><table>
|
||||
<thead><th>Name</th><th>Index</th></thead>
|
||||
<tbody>
|
||||
// highlight-start
|
||||
@for(row of rows; track $index) { <tr>
|
||||
<td>{{row.Name}}</td>
|
||||
<td>{{row.Index}}</td>
|
||||
</tr> }
|
||||
// highlight-end
|
||||
</tbody>
|
||||
</table></div>
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
#### Exporting Data
|
||||
|
||||
The [`writeFile`](/docs/api/write-options) and [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input)
|
||||
functions simplify exporting data. They are best used in the function bodies of
|
||||
event handlers attached to button or other elements.
|
||||
|
||||
A callback can generate a local file when a user clicks a button:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
state((component\nstate))
|
||||
ws(SheetJS\nWorksheet)
|
||||
wb(SheetJS\nWorkbook)
|
||||
file[(XLSX\nexport)]
|
||||
state --> |json_to_sheet\n\n| ws
|
||||
ws --> |book_new\nbook_append_sheet| wb
|
||||
wb --> |writeFile\n\n| file
|
||||
```
|
||||
|
||||
```ts title="src/app/app.component.ts"
|
||||
import { Component } from '@angular/core';
|
||||
import { utils, writeFileXLSX } from 'xlsx';
|
||||
|
||||
interface President { Name: string; Index: number };
|
||||
|
||||
@Component({ /* ... component configuration options ... */ })
|
||||
export class AppComponent {
|
||||
rows: President[] = [ { Name: "SheetJS", Index: 0 }];
|
||||
/* get state data and export to XLSX */
|
||||
onSave(): void {
|
||||
/* generate worksheet from state */
|
||||
// highlight-next-line
|
||||
const ws = utils.json_to_sheet(this.rows);
|
||||
/* create workbook and append worksheet */
|
||||
const wb = utils.book_new();
|
||||
utils.book_append_sheet(wb, ws, "Data");
|
||||
/* export to XLSX */
|
||||
writeFileXLSX(wb, "SheetJSAngularAoO.xlsx");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Complete Component
|
||||
|
||||
This complete component example fetches a test file and displays the contents in
|
||||
a HTML table. When the export button is clicked, a callback will export a file:
|
||||
|
||||
<Tabs groupId="ngVer">
|
||||
<TabItem value="2" label="Angular 2-16">
|
||||
|
||||
```ts title="src/app/app.component.ts"
|
||||
import { Component } from '@angular/core';
|
||||
@ -152,11 +353,72 @@ export class AppComponent {
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="17" label="Angular 17+">
|
||||
|
||||
```ts title="src/app/app.component.ts"
|
||||
import { Component } from '@angular/core';
|
||||
import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
|
||||
interface President { Name: string; Index: number };
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
standalone: true,
|
||||
template: `
|
||||
<div class="content" role="main"><table>
|
||||
<thead><th>Name</th><th>Index</th></thead>
|
||||
<tbody>
|
||||
// highlight-start
|
||||
@for(row of rows; track $index) {
|
||||
<tr>
|
||||
<td>{{row.Name}}</td>
|
||||
<td>{{row.Index}}</td>
|
||||
</tr>
|
||||
}
|
||||
// highlight-end
|
||||
</tbody><tfoot>
|
||||
<button (click)="onSave()">Export XLSX</button>
|
||||
</tfoot>
|
||||
</table></div>
|
||||
`
|
||||
})
|
||||
export class AppComponent {
|
||||
// highlight-next-line
|
||||
rows: President[] = [ { Name: "SheetJS", Index: 0 }];
|
||||
ngOnInit(): void { (async() => {
|
||||
/* Download from https://sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
/* parse workbook */
|
||||
// highlight-next-line
|
||||
const wb = read(ab);
|
||||
|
||||
/* update data */
|
||||
// highlight-next-line
|
||||
this.rows = utils.sheet_to_json<President>(wb.Sheets[wb.SheetNames[0]]);
|
||||
|
||||
})(); }
|
||||
/* get state data and export to XLSX */
|
||||
onSave(): void {
|
||||
// highlight-next-line
|
||||
const ws = utils.json_to_sheet(this.rows);
|
||||
const wb = utils.book_new();
|
||||
utils.book_append_sheet(wb, ws, "Data");
|
||||
writeFileXLSX(wb, "SheetJSAngularAoO.xlsx");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details open><summary><b>How to run the example</b> (click to show)</summary>
|
||||
|
||||
:::note
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was last run on 2023-10-22 using Angular CLI `16.2.7`
|
||||
This demo was last run on 2023-11-18 using Angular 17.0.3 and CLI `17.0.1`
|
||||
|
||||
:::
|
||||
|
||||
@ -169,7 +431,7 @@ npx @angular/cli analytics disable -g
|
||||
1) Create a new project:
|
||||
|
||||
```bash
|
||||
npx @angular/cli@16.2.7 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
npx @angular/cli@17.0.1 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
```
|
||||
|
||||
2) Install the SheetJS dependency and start the dev server:
|
||||
@ -184,7 +446,9 @@ npm start`}
|
||||
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:4200`)
|
||||
|
||||
4) Replace `src/app/app.component.ts` with the previous code snippet.
|
||||
4) In the previous `src/app/app.component.ts` code snippet, select the tab for
|
||||
the appropriate version of Angular ("Angular 2-16" or "Angular 17+"), copy the
|
||||
code contents and replace `src/app/app.component.ts` in the project.
|
||||
|
||||
The page will refresh and show a table with an Export button. Click the button
|
||||
and the page will attempt to download `SheetJSAngularAoO.xlsx`. Open the file
|
||||
@ -198,10 +462,23 @@ npm run build
|
||||
|
||||
To test the generated site, start a web server:
|
||||
|
||||
<Tabs groupId="ngVer">
|
||||
<TabItem value="2" label="Angular 2-16">
|
||||
|
||||
```bash
|
||||
npx -y http-server dist/sheetjs-angular/
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="17" label="Angular 17+">
|
||||
|
||||
```bash
|
||||
npx -y http-server dist/sheetjs-angular/browser/
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Access `http://localhost:8080` with a web browser to test the bundled site.
|
||||
|
||||
</details>
|
||||
@ -215,7 +492,10 @@ However, this does not handle merge cells well!
|
||||
The `sheet_to_html` function generates HTML that is aware of merges and other
|
||||
worksheet features. The generated HTML does not contain any `<script>` tags,
|
||||
and should therefore be safe to pass to an `innerHTML`-bound variable, but the
|
||||
`DomSanitizer` approach is strongly recommended:
|
||||
`DomSanitizer` approach[^5] is strongly recommended:
|
||||
|
||||
<Tabs groupId="ngVer">
|
||||
<TabItem value="2" label="Angular 2-16">
|
||||
|
||||
```ts title="src/app/app.component.ts"
|
||||
import { Component, ElementRef, ViewChild } from '@angular/core';
|
||||
@ -260,11 +540,61 @@ export class AppComponent {
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="17" label="Angular 17+">
|
||||
|
||||
```ts title="src/app/app.component.ts"
|
||||
import { Component, ElementRef, ViewChild } from '@angular/core';
|
||||
// highlight-next-line
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
import { read, utils, writeFileXLSX } from 'xlsx';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
standalone: true,
|
||||
// highlight-next-line
|
||||
template: `<div class="content" role="main" [innerHTML]="html" #tableau></div>
|
||||
<button (click)="onSave()">Export XLSX</button>`
|
||||
})
|
||||
export class AppComponent {
|
||||
// highlight-start
|
||||
constructor(private sanitizer: DomSanitizer) {}
|
||||
html: SafeHtml = "";
|
||||
@ViewChild('tableau') tabeller!: ElementRef<HTMLDivElement>;
|
||||
// highlight-end
|
||||
ngOnInit(): void { (async() => {
|
||||
/* Download from https://sheetjs.com/pres.numbers */
|
||||
const f = await fetch("https://sheetjs.com/pres.numbers");
|
||||
const ab = await f.arrayBuffer();
|
||||
|
||||
/* parse workbook */
|
||||
const wb = read(ab);
|
||||
|
||||
/* update data */
|
||||
// highlight-start
|
||||
const h = utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
|
||||
this.html = this.sanitizer.bypassSecurityTrustHtml(h);
|
||||
// highlight-end
|
||||
})(); }
|
||||
/* get live table and export to XLSX */
|
||||
onSave(): void {
|
||||
// highlight-start
|
||||
const elt = this.tabeller.nativeElement.getElementsByTagName("TABLE")[0];
|
||||
const wb = utils.table_to_book(elt);
|
||||
// highlight-end
|
||||
writeFileXLSX(wb, "SheetJSAngularHTML.xlsx");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<details open><summary><b>How to run the example</b> (click to show)</summary>
|
||||
|
||||
:::note
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was last run on 2023-10-22 using Angular CLI `16.2.7`
|
||||
This demo was last run on 2023-11-18 using Angular 17.0.3 and CLI `17.0.1`
|
||||
|
||||
:::
|
||||
|
||||
@ -277,7 +607,7 @@ npx @angular/cli analytics disable -g
|
||||
1) Create a new project:
|
||||
|
||||
```bash
|
||||
npx @angular/cli@16.2.7 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
npx @angular/cli@17.0.1 new --minimal --defaults --no-interactive sheetjs-angular
|
||||
```
|
||||
|
||||
2) Install the SheetJS dependency and start the dev server:
|
||||
@ -292,7 +622,9 @@ npm start`}
|
||||
|
||||
3) Open a web browser and access the displayed URL (`http://localhost:4200`)
|
||||
|
||||
4) Replace `src/app/app.component.ts` with the previous code snippet.
|
||||
4) In the previous `src/app/app.component.ts` code snippet, select the tab for
|
||||
the appropriate version of Angular ("Angular 2-16" or "Angular 17+"), copy the
|
||||
code contents and replace `src/app/app.component.ts` in the project.
|
||||
|
||||
The page will refresh and show a table with an Export button. Click the button
|
||||
and the page will attempt to download `SheetJSAngularHTML.xlsx`. Open the file
|
||||
@ -306,10 +638,23 @@ npm run build
|
||||
|
||||
To test the generated site, start a web server:
|
||||
|
||||
<Tabs groupId="ngVer">
|
||||
<TabItem value="2" label="Angular 2-16">
|
||||
|
||||
```bash
|
||||
npx -y http-server dist/sheetjs-angular/
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="17" label="Angular 17+">
|
||||
|
||||
```bash
|
||||
npx -y http-server dist/sheetjs-angular/browser/
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Access `http://localhost:8080` with a web browser to test the bundled site.
|
||||
|
||||
</details>
|
||||
@ -654,4 +999,10 @@ will have an `ng-version` attribute.
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
```
|
||||
|
||||
[^1]: The main website for Angular versions 2-16 is <https://angular.io/> . The project moved to a new domain <https://angular.dev/> during the Angular 17 launch.
|
||||
[^2]: See `OnInit` in the [Angular 2-16 docs](https://angular.io/api/core/OnInit) or [Angular 17 docs](https://angular.dev/guide/components/lifecycle#ngoninit)
|
||||
[^3]: See [`ngFor`](https://angular.io/api/common/NgFor) in the Angular 2-16 docs.
|
||||
[^4]: See [`@for`](https://angular.dev/api/core/@for) in the Angular 17 docs.
|
||||
[^5]: See `DomSanitizer` in the [Angular 2-16 docs](https://angular.io/api/platform-browser/DomSanitizer) or [Angular 17 docs](https://angular.dev/api/platform-browser/DomSanitizer)
|
@ -1,5 +1,7 @@
|
||||
---
|
||||
title: AngularJS
|
||||
title: Sheets in AngularJS Sites
|
||||
sidebar_label: AngularJS
|
||||
description: Build interactive websites with AngularJS. Seamlessly integrate spreadsheets into your app using SheetJS. Bring Excel-powered workflows and data to the modern web.
|
||||
pagination_prev: demos/index
|
||||
pagination_next: demos/grid/index
|
||||
sidebar_position: 7
|
||||
@ -19,11 +21,18 @@ This demo is for the legacy AngularJS framework (version 1).
|
||||
|
||||
[AngularJS](https://angularjs.org/) is a JS library for building user interfaces.
|
||||
|
||||
## Demo
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
:::note
|
||||
This demo uses AngularJS and SheetJS to process and generate spreadsheets. We'll
|
||||
explore how to load SheetJS in AngularJS projects and compare common state
|
||||
models and data flow strategies.
|
||||
|
||||
This demo was last run on 2023 August 27 using AngularJS `1.8.2`
|
||||
## Live Demo
|
||||
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was last run on 2023 November 19 using AngularJS `1.8.2`
|
||||
|
||||
:::
|
||||
|
||||
@ -38,27 +47,94 @@ input element for loading user-submitted files.
|
||||
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
can be referenced in a `SCRIPT` tag from the HTML entry point page.
|
||||
|
||||
The `$http` service can request binary data using `"arraybuffer"` response type.
|
||||
This maps to the `"array"` input format for `XLSX.read`:
|
||||
The script adds the `XLSX` global variable.
|
||||
|
||||
## Data Sources
|
||||
|
||||
Modern browsers support a number of convenient APIs for receiving files and
|
||||
allowing users to submit files.
|
||||
|
||||
AngularJS, relevant in an era before the APIs were available, provides wrappers
|
||||
to simplify network and file processing.
|
||||
|
||||
### Remote Files
|
||||
|
||||
To download files from a remote location, the `$http` service can perform GET
|
||||
requests[^1]
|
||||
|
||||
The `responseType` option is directly passed to `XMLHttpRequest`. Setting the
|
||||
property to `"arraybuffer"` ensures the result is an `ArrayBuffer` object.
|
||||
|
||||
The SheetJS [`read`](/docs/api/parse-options) method can parse the `ArrayBuffer`
|
||||
and return a SheetJS workbook object[^2].
|
||||
|
||||
The following controller fetches [a sample file](https://sheetjs.com/pres.xlsx),
|
||||
parses the result into a workbook, extracts the first worksheet, and uses the
|
||||
SheetJS [`sheet_to_html`](/docs/api/utilities/html#html-table-output) method to
|
||||
generate a HTML table:
|
||||
|
||||
```js
|
||||
/* The controller function must accept a `$http` argument */
|
||||
app.controller('sheetjs', function($scope, $http) {
|
||||
/* fetch https://sheetjs.com/pres.xlsx */
|
||||
$http({
|
||||
method:'GET', url:'https://sheetjs.com/pres.xlsx',
|
||||
// highlight-next-line
|
||||
/* ensure the result is an ArrayBuffer object */
|
||||
responseType:'arraybuffer'
|
||||
}).then(function(data) {
|
||||
// highlight-next-line
|
||||
var wb = XLSX.read(data.data, {type:"array"});
|
||||
/* DO SOMETHING WITH wb HERE */
|
||||
$scope.data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
var wb = XLSX.read(data.data);
|
||||
/* generate HTML from first worksheet*/
|
||||
var ws = wb.Sheets[wb.SheetNames[0]];
|
||||
var html = XLSX.utils.sheet_to_html(ws);
|
||||
/* assign to the `tbl` scope property */
|
||||
$scope.tbl = html;
|
||||
}, function(err) { console.log(err); });
|
||||
});
|
||||
```
|
||||
|
||||
<details><summary><b>Parsing User-Submitted Files</b> (click to show)</summary>
|
||||
### User-Submitted Files
|
||||
|
||||
For file input elements, a general import directive is fairly straightforward:
|
||||
Users can submit files using HTML file input elements. A DOM `change` event is
|
||||
created when users select a file.
|
||||
|
||||
In AngularJS, standard DOM event handlers are created using custom directives
|
||||
with the `link` option[^3].
|
||||
|
||||
The following directive function creates a `change` event handler that will use
|
||||
a `FileReader` to generate an `ArrayBuffer` object with the file data, parse the
|
||||
file data using the SheetJS [`read`](/docs/api/parse-options) method, generate a
|
||||
HTML table using [`sheet_to_html`](/docs/api/utilities/html#html-table-output),
|
||||
and store the result in the `tbl` property of the app state:
|
||||
|
||||
```js
|
||||
function SheetJSImportDirective() { return {
|
||||
scope: false,
|
||||
/* $elm will be a reference to the file input DOM element */
|
||||
link: function ($scope, $elm) {
|
||||
/* add a `change` event handler */
|
||||
$elm.on('change', function (changeEvent) {
|
||||
/* use a FileReader to read the file */
|
||||
var reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
/* this event handler will be called once the data is read */
|
||||
var wb = XLSX.read(e.target.result);
|
||||
|
||||
/* generate HTML from first worksheet*/
|
||||
var ws = wb.Sheets[wb.SheetNames[0]];
|
||||
var html = XLSX.utils.sheet_to_html(ws);
|
||||
|
||||
/* assign to the `tbl` scope property */
|
||||
$scope.apply(function() { $scope.tbl = html; });
|
||||
};
|
||||
/* read */
|
||||
reader.readAsArrayBuffer(changeEvent.target.files[0]);
|
||||
});
|
||||
}
|
||||
}; }
|
||||
```
|
||||
|
||||
This functionality can be added to the app in two steps:
|
||||
|
||||
1) Add an `INPUT` element with attribute `import-sheet-js=""`:
|
||||
|
||||
@ -66,35 +142,18 @@ For file input elements, a general import directive is fairly straightforward:
|
||||
<input type="file" import-sheet-js="" multiple="false" />
|
||||
```
|
||||
|
||||
2) Define the `SheetJSImportDirective` directive function:
|
||||
|
||||
```js
|
||||
function SheetJSImportDirective() { return {
|
||||
scope: false,
|
||||
link: function ($scope, $elm) {
|
||||
$elm.on('change', function (changeEvent) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
var wb = XLSX.read(e.target.result);
|
||||
|
||||
/* DO SOMETHING WITH wb HERE */
|
||||
$scope.apply(function() {
|
||||
$scope.data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
});
|
||||
};
|
||||
reader.readAsArrayBuffer(changeEvent.target.files[0]);
|
||||
});
|
||||
}
|
||||
}; }
|
||||
```
|
||||
|
||||
3) Define the `importSheetJs` directive in the app:
|
||||
2) Define the `importSheetJs` directive that attaches `SheetJSImportDirective`:
|
||||
|
||||
```js
|
||||
app.directive("importSheetJs", [SheetJSImportDirective]);
|
||||
```
|
||||
|
||||
</details>
|
||||
:::note pass
|
||||
|
||||
AngularJS normalizes the hyphenated attribute `import-sheet-js` to the
|
||||
`importSheetJs` camel-case directive name.
|
||||
|
||||
:::
|
||||
|
||||
## Internal State
|
||||
|
||||
@ -103,25 +162,15 @@ depends on the application.
|
||||
|
||||
### Array of Objects
|
||||
|
||||
Typically, some users will create a spreadsheet with source data that should be
|
||||
loaded into the site. This sheet will have known columns. For example, our
|
||||
[presidents sheet](https://sheetjs.com/pres.xlsx) has "Name" / "Index" columns:
|
||||
The example [presidents sheet](https://sheetjs.com/pres.xlsx) has one header row
|
||||
with "Name" and "Index" columns. The natural JS representation is an object for
|
||||
each row, using the values in the first rows as keys:
|
||||
|
||||
<table><thead><tr><th>Spreadsheet</th><th>State</th></tr></thead><tbody><tr><td>
|
||||
|
||||
![`pres.xlsx` data](pathname:///pres.png)
|
||||
|
||||
This naturally maps to an array of typed objects, as in the example below:
|
||||
|
||||
```js
|
||||
app.controller('sheetjs', function($scope, $http) {
|
||||
$http({ method:'GET', url:'https://sheetjs.com/pres.xlsx', responseType:'arraybuffer' }).then(function(data) {
|
||||
var wb = XLSX.read(data.data, {type:"array"});
|
||||
// highlight-next-line
|
||||
$scope.data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
}, function(err) { console.log(err); });
|
||||
});
|
||||
```
|
||||
|
||||
`data` will be an array of objects:
|
||||
</td><td>
|
||||
|
||||
```js
|
||||
[
|
||||
@ -133,6 +182,25 @@ app.controller('sheetjs', function($scope, $http) {
|
||||
]
|
||||
```
|
||||
|
||||
</td></tr></tbody></table>
|
||||
|
||||
The SheetJS [`sheet_to_json`](/docs/api/utilities/array#array-output) method
|
||||
generates row objects from worksheets. The following controller parses a remote
|
||||
file, generates row objects, and stores the array in the state:
|
||||
|
||||
```js
|
||||
app.controller('sheetjs', function($scope, $http) {
|
||||
$http({
|
||||
url:'https://sheetjs.com/pres.xlsx',
|
||||
method:'GET', responseType:'arraybuffer'
|
||||
}).then(function(data) {
|
||||
var wb = XLSX.read(data.data);
|
||||
// highlight-next-line
|
||||
$scope.data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
}, function(err) { console.log(err); });
|
||||
});
|
||||
```
|
||||
|
||||
A component will typically loop over the data using `ng-repeat`. The following
|
||||
template generates a TABLE with a row for each President:
|
||||
|
||||
@ -146,7 +214,8 @@ template generates a TABLE with a row for each President:
|
||||
</table>
|
||||
```
|
||||
|
||||
`XLSX.utils.json_to_sheet` can generate a worksheet from the data:
|
||||
The [`json_to_sheet`](/docs/api/utilities/array#array-of-objects-input) method
|
||||
can generate a worksheet from the data:
|
||||
|
||||
```js
|
||||
/* assuming $scope.data is an array of objects */
|
||||
@ -198,11 +267,10 @@ app.controller('sheetjs', function($scope, $http) {
|
||||
XLSX.writeFile(wb, "SheetJSAngularJSAoO.xlsx");
|
||||
};
|
||||
$http({
|
||||
method:'GET',
|
||||
url:'https://sheetjs.com/pres.xlsx',
|
||||
responseType:'arraybuffer'
|
||||
method:'GET', responseType:'arraybuffer'
|
||||
}).then(function(data) {
|
||||
var wb = XLSX.read(data.data, {type:"array"});
|
||||
var wb = XLSX.read(data.data);
|
||||
var data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
||||
$scope.data = data;
|
||||
}, function(err) { console.log(err); });
|
||||
@ -238,8 +306,11 @@ requires the `ngSanitize` plugin.
|
||||
// highlight-next-line
|
||||
var app = angular.module('s5s', ['ngSanitize']);
|
||||
app.controller('sheetjs', function($scope, $http) {
|
||||
$http({ method:'GET', url:'https://sheetjs.com/pres.xlsx', responseType:'arraybuffer' }).then(function(data) {
|
||||
var wb = XLSX.read(data.data, {type:"array"});
|
||||
$http({
|
||||
url:'https://sheetjs.com/pres.xlsx',
|
||||
method:'GET', responseType:'arraybuffer'
|
||||
}).then(function(data) {
|
||||
var wb = XLSX.read(data.data);
|
||||
// highlight-next-line
|
||||
$scope.data = XLSX.utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
|
||||
}, function(err) { console.log(err); });
|
||||
@ -247,7 +318,7 @@ app.controller('sheetjs', function($scope, $http) {
|
||||
</script>
|
||||
```
|
||||
|
||||
The HTML table can be directly exported with `XLSX.utils.table_to_book`:
|
||||
The HTML table can be directly exported with [`table_to_book`](/docs/api/utilities/html#html-table-input):
|
||||
|
||||
```js
|
||||
$scope.exportSheetJS = function() {
|
||||
@ -291,11 +362,10 @@ app.controller('sheetjs', function($scope, $http) {
|
||||
XLSX.writeFile(wb, "SheetJSAngularJSHTML.xlsx");
|
||||
};
|
||||
$http({
|
||||
method:'GET',
|
||||
url:'https://sheetjs.com/pres.xlsx',
|
||||
responseType:'arraybuffer'
|
||||
method:'GET', responseType:'arraybuffer'
|
||||
}).then(function(data) {
|
||||
var wb = XLSX.read(data.data, {type:"array"});
|
||||
var wb = XLSX.read(data.data);
|
||||
$scope.data = XLSX.utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
|
||||
}, function(err) { console.log(err); });
|
||||
});
|
||||
@ -308,3 +378,7 @@ app.controller('sheetjs', function($scope, $http) {
|
||||
URL with a web browser (typically `http://localhost:8080`)
|
||||
|
||||
</details>
|
||||
|
||||
[^1]: See [`$http`](https://docs.angularjs.org/api/ng/service/$http) in the AngularJS documentation.
|
||||
[^2]: See ["Workbook Object"](/docs/csf/book)
|
||||
[^3]: See ["Creating Directives"](https://docs.angularjs.org/guide/directive#creating-a-directive-that-manipulates-the-dom) in the AngularJS documentation.
|
@ -257,7 +257,7 @@ This demo was tested in the following environments:
|
||||
|
||||
| OS and Version | Arch | Tauri | Date |
|
||||
|:---------------|:-----|:---------|:-----------|
|
||||
| macOS 13.5.1 | x64 | `v1.5.0` | 2023-09-30 |
|
||||
| macOS 14.1.1 | x64 | `v1.5.6` | 2023-11-17 |
|
||||
| macOS 14.0 | ARM | `v1.5.2` | 2023-10-18 |
|
||||
| Windows 10 | x64 | `v1.5.0` | 2023-10-01 |
|
||||
| Windows 11 | ARM | `v1.4.1` | 2023-09-26 |
|
||||
@ -287,14 +287,15 @@ If required dependencies are installed, the output will show a checkmark next to
|
||||
|
||||
```
|
||||
[✔] Environment
|
||||
- OS: Mac OS 13.5.1 X64
|
||||
- OS: Mac OS 14.1.1 X64
|
||||
✔ Xcode Command Line Tools: installed
|
||||
✔ rustc: 1.72.1 (d5c2e9c34 2023-09-13)
|
||||
✔ Cargo: 1.72.1 (103a7ff2e 2023-08-15)
|
||||
✔ rustup: 1.26.0+1046 (d4c684485 2023-08-30)
|
||||
✔ rustc: 1.74.0 (79e9716c9 2023-11-13)
|
||||
✔ cargo: 1.74.0 (ecb9851af 2023-10-18)
|
||||
✔ rustup: 1.26.0+198 (393e187b7 2023-11-16)
|
||||
✔ Rust toolchain: stable-x86_64-apple-darwin (default)
|
||||
- node: 16.20.2
|
||||
- npm: 8.19.4
|
||||
- node: 20.9.0
|
||||
- npm: 10.1.0
|
||||
- bun: 1.0.2
|
||||
```
|
||||
|
||||
:::caution pass
|
||||
@ -309,17 +310,9 @@ build step will correctly detect the platform architecture.
|
||||
1) Create a new Tauri app:
|
||||
|
||||
```bash
|
||||
npm create tauri-app
|
||||
npm create tauri-app@latest -- -m npm -t vue-ts SheetJSTauri -y
|
||||
```
|
||||
|
||||
When prompted:
|
||||
|
||||
- Project Name: `SheetJSTauri`
|
||||
- Choose which language to use for your frontend: `TypeScript / JavaScript`
|
||||
- Choose your package manager: `npm`
|
||||
- Choose your UI template: `Vue`
|
||||
- Choose your UI flavor: `TypeScript`
|
||||
|
||||
2) Enter the directory and install dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
@ -378,17 +371,7 @@ At the end, it will print the path to the generated installer.
|
||||
|
||||
:::info pass
|
||||
|
||||
During the last Linux test, the build had failed with an error:
|
||||
|
||||
```
|
||||
'openssl/opensslv.h' file not found
|
||||
```
|
||||
|
||||
This error was resolved installing OpenSSL. On Arch Linux and HoloOS:
|
||||
|
||||
```bash
|
||||
sudo pacman -S openssl
|
||||
```
|
||||
If the build fails, see ["Troubleshooting"](#troubleshooting) for more details.
|
||||
|
||||
:::
|
||||
|
||||
@ -428,6 +411,51 @@ The following features should be manually verified:
|
||||
- Edit the file in a spreadsheet editor, then click "Load Data" and select the
|
||||
edited file. The table will refresh with new contents.
|
||||
|
||||
#### Troubleshooting
|
||||
|
||||
:::note pass
|
||||
|
||||
During the last Linux test, the build failed with the following error message:
|
||||
|
||||
```
|
||||
'openssl/opensslv.h' file not found
|
||||
```
|
||||
|
||||
This error was resolved installing OpenSSL. On Arch Linux and HoloOS:
|
||||
|
||||
```bash
|
||||
sudo pacman -S openssl
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
:::note pass
|
||||
|
||||
During the last macOS test, the build failed with the following error message:
|
||||
|
||||
```
|
||||
Error failed to bundle project: error running bundle_dmg.sh
|
||||
```
|
||||
|
||||
The root cause of the error can be discovered by running
|
||||
|
||||
```bash
|
||||
npm run tauri build -- --verbose
|
||||
```
|
||||
|
||||
The most recent test failed with a message:
|
||||
|
||||
```
|
||||
execution error: Not authorized to send Apple events to Finder
|
||||
```
|
||||
|
||||
This error was resolved by allowing Terminal to control Finder.
|
||||
|
||||
In the "System Settings" app, select "Privacy & Security" in the left column and
|
||||
select "Automation" in the body. Look for "Terminal", expand the section, and enable "Finder".
|
||||
|
||||
:::
|
||||
|
||||
[^1]: See ["Security"](https://tauri.app/v1/references/architecture/security#allowing-api) in the Tauri documentation
|
||||
[^2]: See [`FsAllowlistConfig`](https://tauri.app/v1/api/config/#fsallowlistconfig) in the Tauri documentation
|
||||
[^3]: See [`DialogAllowlistConfig`](https://tauri.app/v1/api/config/#dialogallowlistconfig) in the Tauri documentation
|
||||
|
@ -208,10 +208,10 @@ const aoa = [ ["Hash"], [key] ].concat(Object.entries(values));
|
||||
|
||||
## Complete Example
|
||||
|
||||
:::note
|
||||
:::note Tested Deployments
|
||||
|
||||
This demo was last tested on 2023 August 22 with Redis 7.2.0, Redis connector
|
||||
module 4.6.7 and NodeJS 20.5.1.
|
||||
This demo was last tested on 2023 November 18 with Redis 7.2.3, Redis connector
|
||||
module 4.6.10 and NodeJS 20.9.0.
|
||||
|
||||
:::
|
||||
|
||||
@ -225,7 +225,7 @@ this demo also requires NodeJS version 18 or later.
|
||||
|
||||
0) Set up and start a local Redis server.
|
||||
|
||||
:::note
|
||||
:::note pass
|
||||
|
||||
This demo was last tested on macOS. Redis was installed with:
|
||||
|
||||
@ -254,7 +254,7 @@ curl -LO https://docs.sheetjs.com/nosql/SheetJSRedisTest.mjs
|
||||
2) Install dependencies:
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz redis@4.6.7`}
|
||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz redis@4.6.10`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Run the test script:
|
||||
|
@ -27,7 +27,7 @@ This demo was verified by NetSuite consultants in the following deployments:
|
||||
| ScheduledScript | 2.1 | 2023-08-18 |
|
||||
| Restlet | 2.1 | 2023-10-05 |
|
||||
| Suitelet | 2.1 | 2023-10-27 |
|
||||
| MapReduceScript | 2.1 | 2023-07-31 |
|
||||
| MapReduceScript | 2.1 | 2023-11-16 |
|
||||
|
||||
:::
|
||||
|
||||
|
@ -33,7 +33,7 @@ in the [issue tracker](https://git.sheetjs.com/sheetjs/docs.sheetjs.com/issues)
|
||||
- [`React`](/docs/demos/frontend/react)
|
||||
- [`Svelte`](/docs/demos/frontend/svelte)
|
||||
- [`VueJS`](/docs/demos/frontend/vue)
|
||||
- [`Angular.JS`](/docs/demos/frontend/angularjs)
|
||||
- [`AngularJS`](/docs/demos/frontend/angularjs)
|
||||
- [`Dojo`](/docs/demos/frontend/legacy#dojo-toolkit)
|
||||
- [`Knockout`](/docs/demos/frontend/legacy#knockoutjs)
|
||||
|
||||
|
2
docz/static/dta/dta.min.js
vendored
2
docz/static/dta/dta.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user