angularjs

This commit is contained in:
SheetJS 2023-04-08 20:20:50 -04:00
parent 00d44fcfbb
commit 76543faea9
6 changed files with 373 additions and 162 deletions

@ -0,0 +1,307 @@
---
title: AngularJS
pagination_prev: demos/index
pagination_next: demos/grid/index
sidebar_position: 7
---
:::warning
This demo is for the legacy AngularJS framework (version 1).
"Angular" now commonly refers to the new framework starting with version 2.
[The "Angular" demo](/docs/demos/frontend/angular) covers the new framework.
:::
AngularJS is a JS library for building user interfaces.
## Demo
:::note
This demo was last run on 2023 April 08 using AngularJS `1.5.0`
:::
[Click here for a live standalone integration demo.](pathname:///angularjs/)
The demo uses an array of objects as its internal state. It fetches and displays
data on load. It also includes a button for exporting data to file and a file
input element for loading user-submitted files.
## Installation
The [Standalone scripts](/docs/getting-started/installation/standalone) can be
referenced in a `SCRIPT` tag from the HTML entrypoint page.
The `$http` service can request binary data using `"arraybuffer"` response type.
This maps to the `"array"` input format for `XLSX.read`:
```js
app.controller('sheetjs', function($scope, $http) {
$http({
method:'GET', url:'https://sheetjs.com/pres.xlsx',
// highlight-next-line
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]]);
}, function(err) { console.log(err); });
});
```
<details><summary><b>Parsing User-Submitted Files</b> (click to show)</summary>
For file input elements, a general import directive is fairly straightforward:
1) Add an `INPUT` element with attribute `import-sheet-js=""`:
```html
<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:
```js
app.directive("importSheetJs", [SheetJSImportDirective]);
```
</details>
## Internal State
The various SheetJS APIs work with various data shapes. The preferred state
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:
![`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:
```js
[
{ Name: "Bill Clinton", Index: 42 },
{ Name: "GeorgeW Bush", Index: 43 },
{ Name: "Barack Obama", Index: 44 },
{ Name: "Donald Trump", Index: 45 },
{ Name: "Joseph Biden", Index: 46 }
]
```
A component will typically loop over the data using `ng-repeat`. The following
template generates a TABLE with a row for each President:
```html
<table id="sjs-table">
<tr><th>Name</th><th>Index</th></tr>
<tr ng-repeat="row in data track by $index">
<td>{{row.Name}}</td>
<td>{{row.Index}}</td>
</tr>
</table>
```
`XLSX.utils.json_to_sheet` can generate a worksheet from the data:
```js
/* assuming $scope.data is an array of objects */
$scope.exportSheetJS = function() {
/* generate a worksheet */
var ws = XLSX.utils.json_to_sheet($scope.data);
/* add to workbook */
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Presidents");
/* write workbook and force a download */
XLSX.writeFile(wb, "SheetJSAngularJSAoO.xlsx");
};
```
<details><summary><b>Complete Example</b> (click to show)</summary>
1) Save the following to `index.html`:
```html title="index.html"
<!DOCTYPE html>
<html ng-app="s5s">
<head>
<title>SheetJS + AngularJS</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js"></script>
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
</head>
<body>
<h3><a href="https://sheetjs.com">SheetJS + AngularJS demo</a></h3>
<div ng-controller="sheetjs">
<button ng-click="exportSheetJS()">Export Table</button>
<table id="s5s-table">
<tr><th>Name</th><th>Index</th></tr>
<tr ng-repeat="row in data track by $index">
<td>{{row.Name}}</td>
<td>{{row.Index}}</td>
</tr>
</table>
</div>
<script>
var app = angular.module('s5s', []);
app.controller('sheetjs', function($scope, $http) {
$scope.exportSheetJS = function() {
var ws = XLSX.utils.json_to_sheet($scope.data);
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Presidents");
XLSX.writeFile(wb, "SheetJSAngularJSAoO.xlsx");
};
$http({
method:'GET',
url:'https://sheetjs.com/pres.xlsx',
responseType:'arraybuffer'
}).then(function(data) {
var wb = XLSX.read(data.data, {type:"array"});
var data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
$scope.data = data;
}, function(err) { console.log(err); });
});
</script>
</body>
</html>
```
2) Start a local web server with `npx http-server .` and access the displayed
URL with a web browser (typically `http://localhost:8080`)
</details>
### HTML
The main disadvantage of the Array of Objects approach is the specific nature
of the columns. For more general use, passing around an Array of Arrays works.
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 `ng-bind-html` binding. This approach
requires the `ngSanitize` plugin.
```html
<div ng-controller="sheetjs">
<!-- highlight-next-line -->
<div ng-bind-html="data" id="tbl"></div>
</div>
<script>
// 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"});
// highlight-next-line
$scope.data = XLSX.utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
}, function(err) { console.log(err); });
});
</script>
```
The HTML table can be directly exported with `XLSX.utils.table_to_book`:
```js
$scope.exportSheetJS = function() {
/* export table element */
// highlight-start
var tbl = document.getElementById("tbl").getElementsByTagName("TABLE")[0];
var wb = XLSX.utils.table_to_book(tbl);
// highlight-end
XLSX.writeFile(wb, "SheetJSAngularJSHTML.xlsx");
};
```
<details><summary><b>Complete Example</b> (click to show)</summary>
1) Save the following to `index.html`:
```html title="index.html"
<!DOCTYPE html>
<html ng-app="s5s">
<head>
<title>SheetJS + AngularJS</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-sanitize.js"></script>
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js"></script>
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
</head>
<body>
<h3><a href="https://sheetjs.com">SheetJS + AngularJS demo</a></h3>
<div ng-controller="sheetjs">
<button ng-click="exportSheetJS()">Export Table</button>
<div ng-bind-html="data" id="tbl"></div>
</div>
<script>
var app = angular.module('s5s', ['ngSanitize']);
app.controller('sheetjs', function($scope, $http) {
$scope.exportSheetJS = function() {
var tbl = document.getElementById("tbl").getElementsByTagName("TABLE")[0];
var wb = XLSX.utils.table_to_book(tbl);
XLSX.writeFile(wb, "SheetJSAngularJSHTML.xlsx");
};
$http({
method:'GET',
url:'https://sheetjs.com/pres.xlsx',
responseType:'arraybuffer'
}).then(function(data) {
var wb = XLSX.read(data.data, {type:"array"});
$scope.data = XLSX.utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
}, function(err) { console.log(err); });
});
</script>
</body>
</html>
```
2) Start a local web server with `npx http-server .` and access the displayed
URL with a web browser (typically `http://localhost:8080`)
</details>

@ -177,134 +177,6 @@ included in the page and the relevant features are enabled on the target system.
## Frameworks
### AngularJS
AngularJS was a front-end MVC framework that was discontinued by Google in 2022.
It should not be confused with the modern framework "Angular".
The [Live demo](pathname:///angularjs/index.html) shows a simple table that is
updated with file data and exported to spreadsheets.
This demo uses AngularJS 1.5.0.
<details><summary><b>Full Exposition</b> (click to show)</summary>
**Array of Objects**
A common data table is often stored as an array of objects:
```js
$scope.data = [
{ Name: "Bill Clinton", Index: 42 },
{ Name: "GeorgeW Bush", Index: 43 },
{ Name: "Barack Obama", Index: 44 },
{ Name: "Donald Trump", Index: 45 }
];
```
This neatly maps to a table with `ng-repeat`:
```html
<table id="sjs-table">
<tr><th>Name</th><th>Index</th></tr>
<tr ng-repeat="row in data">
<td>{{row.Name}}</td>
<td>{{row.Index}}</td>
</tr>
</table>
```
The `$http` service can request binary data using the `"arraybuffer"` response
type coupled with `XLSX.read` with type `"array"`:
```js
$http({
method:'GET',
url:'https://sheetjs.com/pres.xlsx',
responseType:'arraybuffer'
}).then(function(data) {
var wb = XLSX.read(data.data, {type:"array"});
var d = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
$scope.data = d;
}, function(err) { console.log(err); });
```
The HTML table can be directly exported with `XLSX.utils.table_to_book`:
```js
var wb = XLSX.utils.table_to_book(document.getElementById('sjs-table'));
XLSX.writeFile(wb, "export.xlsx");
```
**Import Directive**
A general import directive is fairly straightforward:
- Define the `importSheetJs` directive in the app:
```js
app.directive("importSheetJs", [SheetJSImportDirective]);
```
- Add the attribute `import-sheet-js=""` to the file input element:
```html
<input type="file" import-sheet-js="" multiple="false" />
```
- Define the directive:
```js
function SheetJSImportDirective() {
return {
scope: { opts: '=' },
link: function ($scope, $elm) {
$elm.on('change', function (changeEvent) {
var reader = new FileReader();
reader.onload = function (e) {
/* read workbook */
var ab = e.target.result;
var workbook = XLSX.read(ab);
/* DO SOMETHING WITH workbook HERE */
};
reader.readAsArrayBuffer(changeEvent.target.files[0]);
});
}
};
}
```
**Export Service**
An export can be triggered at any point! Depending on how data is represented,
a workbook object can be built using the utility functions. For example, using
an array of objects:
```js
/* starting from this data */
var data = [
{ name: "Barack Obama", pres: 44 },
{ name: "Donald Trump", pres: 45 }
];
/* generate a worksheet */
var ws = XLSX.utils.json_to_sheet(data);
/* add to workbook */
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Presidents");
/* write workbook and force a download */
XLSX.writeFile(wb, "sheetjs.xlsx");
```
</details>
### Dojo Toolkit
_Live Demos_

@ -27,7 +27,7 @@ Demos for popular frameworks are included in separate pages:
</li>);
})}</ul>
Legacy frameworks including old AngularJS are covered [in the "Legacy" section](/docs/demos/frontend/legacy).
Legacy frameworks including Dojo are covered [in the "Legacy" section](/docs/demos/frontend/legacy).
:::note Recommendation

@ -58,7 +58,7 @@ through a special Export button. It handles the SheetJS operations internally.
This UI Grid is for AngularJS, not the modern Angular. New projects should not
use AngularJS. This demo is included for legacy applications.
The [AngularJS demo](/docs/demos/frontend/legacy#angularjs) covers more general strategies.
The [AngularJS demo](/docs/demos/frontend/angularjs) covers more general strategies.
:::

@ -26,7 +26,7 @@ run in the web browser, demos will include interactive examples.
- [`React`](/docs/demos/frontend/react)
- [`Svelte`](/docs/demos/frontend/svelte)
- [`VueJS`](/docs/demos/frontend/vue)
- [`Angular.JS`](/docs/demos/frontend/legacy#angularjs)
- [`Angular.JS`](/docs/demos/frontend/angularjs)
- [`Dojo`](/docs/demos/frontend/legacy#dojo-toolkit)
- [`Knockout`](/docs/demos/frontend/legacy#knockoutjs)

@ -10,57 +10,89 @@
<!-- SheetJS library -->
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/shim.min.js"></script>
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
<style>
* { padding: 0; margin: 0; box-sizing: border-box; }
html, body { width: 100vw; max-width: 100%; font-size: 16px; font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; -webkit-font-smoothing: antialiased; background: white; color: black; }
body { padding-top: 5px; max-width: 760px; margin-left: auto; margin-right: auto; }
table { border-collapse: collapse; }
table, tr, th, td { border: 1px solid; }
div { padding: 5px; }
li { margin: 5px 0;}
</style>
</head>
<body>
<pre>
<b><a href="https://sheetjs.com">SheetJS + AngularJS demo</a></b>
The core library can be used as-is in AngularJS applications.
The <a href="https://docs.sheetjs.com">Community Edition README</a> details some common use cases.
We also have some <a href="https://docs.sheetjs.com/docs/demos/">more public demos</a>
<h3><a href="https://sheetjs.com">SheetJS + AngularJS demo</a></h3>
<br/>
The core library can be used as-is in AngularJS applications. <a href="https://docs.sheetjs.com/docs/demos/frontend/angularjs">The SheetJS + AngularJS demo</a> covers common strategies.<br/><br/>
This demo shows:
- $http request for XLSX file and scope update with data
- HTML table using ng-repeat
- XLSX table export using `XLSX.utils.table_to_book`
<ul>
<li>$http request for XLSX file and scope update with data</li>
<li>HTML table using ng-repeat</li>
<li>XLSX table export using `XLSX.utils.json_to_sheet`</li>
<li>Custom directive for importing user-submitted files</li>
</ul>
<br/>
<a href="https://sheetjs.com/pres.xlsx">Sample Spreadsheet</a>
<br/><br/>
The table has hardcoded fields `Name` and `Index`.
</pre>
<br/><br/>
<b>Testing</b><br/>
<ol>
<li>Load page. The table should show a list of presidents.</li>
<li>Click "Export Table" and confirm that a file was downloaded.</li>
<li>Open the generated file in a spreadsheet editor and add a new row.</li>
<li>Use the file input element to select the edited file. The table should refresh.</li>
</ol>
<br/>
<div ng-controller="sheetjs">
<table id="s5s-table">
<tr><th>Name</th><th>Index</th></tr>
<tr ng-repeat="row in data">
<td>{{row.Name}}</td>
<td>{{row.Index}}</td>
</tr>
</table>
<button id="exportbtn">Export Table</button>
<input type="file" import-sheet-js="" multiple="false" /><br/>
<table id="s5s-table">
<tr><th>Name</th><th>Index</th></tr>
<tr ng-repeat="row in data track by $index">
<td>{{row.Name}}</td>
<td>{{row.Index}}</td>
</tr>
</table>
<button ng-click="exportSheetJS()">Export Table</button>
</div>
<script>
var app = angular.module('s5s', []);
app.controller('sheetjs', function($scope, $http) {
$scope.exportSheetJS = function() {
var ws = XLSX.utils.json_to_sheet($scope.data);
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Presidents");
XLSX.writeFile(wb, "SheetJSAngularJSExport.xlsx");
};
$http({
method:'GET',
url:'https://sheetjs.com/pres.xlsx',
responseType:'arraybuffer'
}).then(function(data) {
var wb = XLSX.read(data.data, {type:"array"});
var d = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
$scope.data = d;
$scope.data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
}, function(err) { console.log(err); });
});
exportbtn.addEventListener('click', function() {
var wb = XLSX.utils.table_to_book(document.getElementById('s5s-table'));
XLSX.writeFile(wb, "export.xlsx");
});
app.directive("importSheetJs", [SheetJSImportDirective]);
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);
$scope.$apply(function() {
$scope.data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
});
};
reader.readAsArrayBuffer(changeEvent.target.files[0]);
});
}
};
}
</script>
<script type="text/javascript">
/* eslint no-use-before-define:0 */