This commit is contained in:
SheetJS 2023-11-19 00:23:07 -05:00
parent a4ad3195f5
commit 8f9512b217
7 changed files with 587 additions and 134 deletions

@ -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)

File diff suppressed because one or more lines are too long