12 KiB
title | sidebar_label | description | pagination_prev | pagination_next | sidebar_position |
---|---|---|---|---|---|
Sheets in AngularJS Sites | AngularJS | Build interactive websites with AngularJS. Seamlessly integrate spreadsheets into your app using SheetJS. Bring Excel-powered workflows and data to the modern web. | demos/index | demos/grid/index | 7 |
import current from '/version.js'; import CodeBlock from '@theme/CodeBlock';
:::warning pass
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 covers the new framework.
:::
AngularJS is a JS library for building user interfaces.
SheetJS is a JavaScript library for reading and writing data from spreadsheets.
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.
Live Demo
:::note Tested Deployments
This demo was tested in the following environments:
Version | Date |
---|---|
1.8.2 (latest) |
2023-12-04 |
1.2.32 (legacy) |
2023-12-04 |
:::
Click here for a live standalone integration demo.
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 SheetJS Standalone scripts
can be referenced in a SCRIPT
tag from the HTML entry point page.
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
requests1
The responseType
option is directly passed to XMLHttpRequest
. Setting the
property to "arraybuffer"
ensures the result is an ArrayBuffer
object.
The SheetJS read
method can parse the ArrayBuffer
and return a SheetJS workbook object2.
The following controller fetches a sample file,
parses the result into a workbook, extracts the first worksheet, and uses the
SheetJS sheet_to_html
method to
generate a HTML table:
/* 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',
/* ensure the result is an ArrayBuffer object */
responseType:'arraybuffer'
}).then(function(data) {
// highlight-next-line
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); });
});
User-Submitted Files
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
option3.
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
method, generate a
HTML table using sheet_to_html
,
and store the result in the tbl
property of the app state:
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:
- Add an
INPUT
element with attributeimport-sheet-js=""
:
<input type="file" import-sheet-js="" multiple="false" />
- Define the
importSheetJs
directive that attachesSheetJSImportDirective
:
app.directive("importSheetJs", [SheetJSImportDirective]);
:::note pass
AngularJS normalizes the hyphenated attribute import-sheet-js
to the
importSheetJs
camel-case directive name.
:::
Internal State
The various SheetJS APIs work with various data shapes. The preferred state depends on the application.
Array of Objects
The example presidents sheet 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:
Spreadsheet | State |
---|---|
|
The SheetJS sheet_to_json
method
generates row objects from worksheets. The following controller parses a remote
file, generates row objects, and stores the array in the state:
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:
<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>
The json_to_sheet
method
can generate a worksheet from the data:
/* 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");
};
Complete Example (click to show)
- Save the following to
index.html
:
{`\
<html ng-app="s5s"> <head> </head>SheetJS + AngularJS demo
\n\Name | Index |
---|---|
{{row.Name}} | {{row.Index}} |
- Start a local web server with
npx http-server .
and access the displayed URL with a web browser (typicallyhttp://localhost:8080
)
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
plugin4.
<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({
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); });
});
</script>
The HTML table can be directly exported with table_to_book
:
$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");
};
Complete Example (click to show)
- Save the following to
index.html
:
{`\
<html ng-app="s5s"> <head> </head>SheetJS + AngularJS demo
\n\- Start a local web server with
npx http-server .
and access the displayed URL with a web browser (typicallyhttp://localhost:8080
)
-
See "Workbook Object" ↩︎
-
See "Creating Directives" in the AngularJS documentation. ↩︎
-
See
ngSanitize
in the AngularJS documentation. ↩︎