sheetforce
@ -6,7 +6,7 @@ sidebar_custom_props:
|
||||
|
||||
import current from '/version.js';
|
||||
|
||||
# AMD
|
||||
# AMD (define)
|
||||
|
||||
Each standalone release script is available at <https://cdn.sheetjs.com/>.
|
||||
|
||||
@ -72,6 +72,8 @@ require(['./xlsx.full.min'], function(XLSX) {
|
||||
});
|
||||
```
|
||||
|
||||
#### Aliases
|
||||
|
||||
The `requirejs.config` function can define aliases through the `paths` key:
|
||||
|
||||
```js
|
||||
@ -81,3 +83,11 @@ requirejs.config({
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Once that is set, app code can freely require `xlsx`:
|
||||
|
||||
```js
|
||||
require(['xlsx'], function(XLSX) {
|
||||
// ... use XLSX here
|
||||
});
|
||||
```
|
||||
|
378
docz/docs/04-getting-started/03-demos/05-salesforce.md
Normal file
@ -0,0 +1,378 @@
|
||||
---
|
||||
sidebar_position: 6
|
||||
---
|
||||
|
||||
# Salesforce LWC
|
||||
|
||||
Salesforce apps can use third-party libraries in "Lightning Web Components".
|
||||
|
||||
This demo assumes familiarity with Lightning Web Components. Salesforce has a
|
||||
[detailed introduction.](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.get_started_introduction)
|
||||
|
||||
:::caution
|
||||
|
||||
Some of the details may differ across releases of Salesforce. This demo is based
|
||||
on Lightning API version `55.0` and was last tested on 2022 June 26.
|
||||
|
||||
Salesforce may change the platform in backwards-incompatible ways, so the demo
|
||||
may require some adjustments. The official documentation should be consulted.
|
||||
|
||||
:::
|
||||
|
||||
## Getting Started
|
||||
|
||||
This demo was built on a "Developer Edition" account. At the time of writing, an
|
||||
[account can be created for free.](https://developer.salesforce.com/signup)
|
||||
|
||||
### Create Sample Project and Component
|
||||
|
||||
Following the steps in ["Develop in Non-Scratch Orgs"](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.get_started_sfdx_deploy):
|
||||
|
||||
```bash
|
||||
## Login
|
||||
sfdx force:auth:web:login -d -a LWC-Hub
|
||||
|
||||
## Create Sample Project and Component
|
||||
sfdx force:project:create --projectname SheetForce
|
||||
cd SheetForce
|
||||
sfdx force:lightning:component:create --type lwc -n sheetComponent -d force-app/main/default/lwc
|
||||
```
|
||||
|
||||
By default, the component will not be available to app pages. A few files must
|
||||
be changed:
|
||||
|
||||
`force-app\main\default\lwc\sheetComponent\sheetComponent.html` add some HTML:
|
||||
|
||||
```html force-app\main\default\lwc\sheetComponent\sheetComponent.html
|
||||
<template>
|
||||
<!-- highlight-next-line -->
|
||||
<b>SheetForce demo</b>
|
||||
</template>
|
||||
```
|
||||
|
||||
`force-app\main\default\lwc\sheetComponent\sheetComponent.js-meta.xml` change
|
||||
`isExposed` from `false` to `true` and add some metadata:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||
<apiVersion>55.0</apiVersion>
|
||||
<!-- highlight-start -->
|
||||
<isExposed>true</isExposed>
|
||||
<masterLabel>SheetForce</masterLabel>
|
||||
<description>SheetJS Demo</description>
|
||||
<targets>
|
||||
<target>lightning__AppPage</target>
|
||||
</targets>
|
||||
<!-- highlight-end -->
|
||||
</LightningComponentBundle>
|
||||
```
|
||||
|
||||
### Deploy Sample Project
|
||||
|
||||
Deploy the project:
|
||||
|
||||
```bash
|
||||
sfdx force:source:deploy -p force-app -u SALESFORCE@USER.NAME # replace with actual username
|
||||
```
|
||||
|
||||
The custom component can be found in Custom Code > Lightning Components.
|
||||
|
||||
![Custom Component](pathname:///files/sfcustcomp.png)
|
||||
|
||||
### Initialize App Page
|
||||
|
||||
Create an "App Page" in the "Lightning App Builder". Instructions are included
|
||||
in [Hello World in a Scratch Org](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.get_started_sfdx_hello_world)
|
||||
|
||||
The following options should be set:
|
||||
- The "App Page" option should be selected.
|
||||
- The App Label should be set to "SheetJS Demo".
|
||||
- The "One Region" layout should be selected.
|
||||
|
||||
Under Custom components, you should see "SheetForce". Click and drag it into
|
||||
the app builder main view to add it to the page.
|
||||
|
||||
Click "Save" and click "Yes" to activate. The following options should be set:
|
||||
- Click "Change..." next to "Icon" and pick a memorable icon
|
||||
- Under "Lightning Experience" click "LightningBolt" then "Add page to app"
|
||||
|
||||
Click "Save" to activate the page, then click the left arrow to return to Setup.
|
||||
|
||||
Click the App Launcher and select "Bolt Solutions" then "SheetJS Demo". You
|
||||
should see a page like
|
||||
|
||||
![SheetForce Demo](pathname:///files/sfinitial.png)
|
||||
|
||||
|
||||
## Adding the Standalone Script
|
||||
|
||||
The [standalone script](../../installation/standalone) can be downloaded and
|
||||
added as a static resource. Due to Salesforce naming restrictions, it will have
|
||||
to be renamed to `sheetjs.js` when adding the static resource.
|
||||
|
||||
1) Download <https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js>
|
||||
|
||||
:::warning
|
||||
|
||||
**DO NOT "COPY AND PASTE"!** The file should be explicitly downloaded. Copying
|
||||
and pasting corrupts the source code and the component will fail in subtle ways.
|
||||
|
||||
The easiest approach is to right-click the link and select "Save Link As..."
|
||||
|
||||
:::
|
||||
|
||||
2) Move the file to the `force-app/main/default/staticresources/` folder and
|
||||
rename the file to `sheetjs.js`.
|
||||
|
||||
3) Create `force-app/main/default/staticresources/sheetjs.resource-meta.xml`:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<StaticResource xmlns="http://soap.sforce.com/2006/04/metadata">
|
||||
<cacheControl>Private</cacheControl>
|
||||
<contentType>application/javascript</contentType>
|
||||
</StaticResource>
|
||||
```
|
||||
|
||||
4) Deploy the project again:
|
||||
|
||||
```bash
|
||||
sfdx force:source:deploy -p force-app -u SALESFORCE@USER.NAME # replace with actual username
|
||||
```
|
||||
|
||||
:::note
|
||||
|
||||
The official documentation recommends adding a static resource with a ZIP file.
|
||||
That approach is not explored in this demo.
|
||||
|
||||
:::
|
||||
|
||||
Custom Code > Static Resources should now list `sheetjs`:
|
||||
|
||||
![Static Resources](pathname:///files/sfstatic.png)
|
||||
|
||||
### Test the Static Resource
|
||||
|
||||
The script can be loaded from component code with:
|
||||
|
||||
```js
|
||||
import XLSX from '@salesforce/resourceUrl/sheetjs';
|
||||
```
|
||||
|
||||
The library includes a version number that can be displayed:
|
||||
|
||||
1) Add a reference in `sheetComponent.js` and expose the `version` property:
|
||||
|
||||
```js
|
||||
import { LightningElement } from 'lwc';
|
||||
import { loadScript } from 'lightning/platformResourceLoader';
|
||||
// highlight-next-line
|
||||
import sheetjs from '@salesforce/resourceUrl/sheetjs';
|
||||
|
||||
export default class SheetComponent extends LightningElement {
|
||||
version = "???"; // start with ???
|
||||
async connectedCallback() {
|
||||
// highlight-next-line
|
||||
await loadScript(this, sheetjs); // load the library
|
||||
// At this point, the library is accessible with the `XLSX` variable
|
||||
this.version = XLSX.version;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2) Reference the variable in `sheetComponent.html`:
|
||||
|
||||
```html
|
||||
<template>
|
||||
<!-- highlight-next-line -->
|
||||
<b>SheetForce {version}</b>
|
||||
</template>
|
||||
```
|
||||
|
||||
3) Deploy the project again and re-load the Bolt Solutions "SheetJS Demo" page:
|
||||
|
||||
![Version number](pathname:///files/sfversion.png)
|
||||
|
||||
## Exporting Data from SF Lists
|
||||
|
||||
:::note
|
||||
|
||||
There are many different data types and APIs. This demo uses the deprecated
|
||||
`getListUi` function to pull account data.
|
||||
|
||||
:::
|
||||
|
||||
### Steps
|
||||
|
||||
#### Getting Account Data
|
||||
|
||||
The main method to obtain data is `getListUi` and the key for account data is
|
||||
`ACCOUNT_OBJECT`:
|
||||
|
||||
```js
|
||||
import { getListUi } from 'lightning/uiListApi';
|
||||
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
|
||||
|
||||
// ...
|
||||
|
||||
export default class SheetComponent extends LightningElement {
|
||||
@wire(getListUi, {
|
||||
objectApiName: ACCOUNT_OBJECT.objectApiName,
|
||||
listViewApiName: 'AllAccounts'
|
||||
}) listInfo({ error, data }) {
|
||||
|
||||
// LIST DATA AVAILABLE HERE
|
||||
|
||||
};
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### Generating an Array of Arrays
|
||||
|
||||
SheetJS most reliably translates "arrays of arrays", a nested array which
|
||||
directly maps to individual cell addresses. For example:
|
||||
|
||||
```js
|
||||
var data = [
|
||||
["Name", "Phone"], // row 1
|
||||
["Foo Bar", "(555) 555-5555"], // row 2
|
||||
["Baz Qux", "(555) 555-5556"] // row 3
|
||||
];
|
||||
```
|
||||
|
||||
The APIs typically return nested objects, so the array must be constructed.
|
||||
|
||||
<details><summary><b>Salesforce Representation</b> (click to show)</summary>
|
||||
|
||||
The `data` parameter in the callback has a deep structure. Typically one would
|
||||
set a property in the component and display data in a template:
|
||||
|
||||
```js
|
||||
// ...
|
||||
// declare records variable in the component
|
||||
records;
|
||||
@wire(getListUi, {
|
||||
objectApiName: ACCOUNT_OBJECT.objectApiName,
|
||||
listViewApiName: 'AllAccounts'
|
||||
}) listInfo({ error, data }) {
|
||||
if (data) {
|
||||
// data.records.records is the array of interest
|
||||
this.records = data.records.records;
|
||||
this.error = undefined;
|
||||
}
|
||||
}
|
||||
// ...
|
||||
```
|
||||
|
||||
The template itself would iterate across the records:
|
||||
|
||||
```html
|
||||
<template>
|
||||
<template if:true={records}>
|
||||
<table>
|
||||
<tr><th>Name</th><th>Phone</th></tr>
|
||||
<template for:each={records} for:item="record">
|
||||
<tr key={record.fields.Id.value}>
|
||||
<td>{record.fields.Name.value}</td>
|
||||
<td>{record.fields.Phone.value}</td>
|
||||
</tr>
|
||||
</template>
|
||||
</table>
|
||||
</template>
|
||||
</template>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
A suitable SheetJS array of arrays can be constructed by mapping across records:
|
||||
|
||||
```js
|
||||
var headers = [ "Name", "Phone" ];
|
||||
this.aoa = [headers].concat(data.records.records.map(record => [
|
||||
record.fields.Name.value, // Name field
|
||||
record.fields.Phone.value, // Phone field
|
||||
]));
|
||||
```
|
||||
|
||||
This is readily exported to a spreadsheet in a callback function:
|
||||
|
||||
```js
|
||||
@api async download() {
|
||||
await loadScript(this, sheetjs); // load the library
|
||||
// create workbook
|
||||
var wb = XLSX.utils.book_new();
|
||||
var ws = XLSX.utils.aoa_to_sheet(this.aoa);
|
||||
XLSX.utils.book_append_sheet(wb, ws, "Data");
|
||||
// export
|
||||
XLSX.writeFile(wb, "SheetForceExport.xlsx");
|
||||
};
|
||||
```
|
||||
|
||||
### Complete Example
|
||||
|
||||
1) Add a button to `sheetComponent.html` that will call a `download` callback:
|
||||
|
||||
```html
|
||||
<template>
|
||||
<!-- if the `aoa` property is set, show a button -->
|
||||
<template if:true={aoa}>
|
||||
<button onclick={download}><b>Click to Export!</b></button>
|
||||
</template>
|
||||
<!-- if the `aoa` property is not set, show a message -->
|
||||
<template if:false={aoa}><b>Please wait for data to load ...</b></template>
|
||||
</template>
|
||||
```
|
||||
|
||||
2) Replace `sheetComponent.js` with the following:
|
||||
|
||||
```js
|
||||
import { LightningElement, wire, api } from 'lwc';
|
||||
import { loadScript } from 'lightning/platformResourceLoader';
|
||||
import { getListUi } from 'lightning/uiListApi';
|
||||
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
|
||||
|
||||
import sheetjs from '@salesforce/resourceUrl/sheetjs';
|
||||
|
||||
export default class SheetComponent extends LightningElement {
|
||||
aoa; // will hold data for export
|
||||
@wire(getListUi, {
|
||||
objectApiName: ACCOUNT_OBJECT.objectApiName,
|
||||
listViewApiName: 'AllAccounts'
|
||||
}) listInfo({ error, data }) {
|
||||
if (data) {
|
||||
var headers = [ "Name", "Phone" ];
|
||||
// create AOA and assign to `aoa` property
|
||||
this.aoa = [headers].concat(data.records.records.map(record => [
|
||||
record.fields.Name.value, // Name field
|
||||
record.fields.Phone.value, // Phone field
|
||||
]));
|
||||
} else if (error) console.log(error);
|
||||
};
|
||||
@api async download() {
|
||||
await loadScript(this, sheetjs); // load the library
|
||||
// create workbook
|
||||
var wb = XLSX.utils.book_new();
|
||||
var ws = XLSX.utils.aoa_to_sheet(this.aoa);
|
||||
XLSX.utils.book_append_sheet(wb, ws, "Data");
|
||||
// export
|
||||
XLSX.writeFile(wb, "SheetForceExport.xlsx");
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
3) Re-deploy and refresh the app page:
|
||||
|
||||
![SF Export Button](pathname:///files/sfexport.png)
|
||||
|
||||
The simple export has all of the data:
|
||||
|
||||
![Excel Export](pathname:///files/sfxlexport.png)
|
||||
|
||||
:::note
|
||||
|
||||
[SheetJS Pro](https://sheetjs.com/pro) offers additional styling options like
|
||||
cell styling, automatic column width calculations, and frozen rows.
|
||||
|
||||
:::
|
@ -35,6 +35,7 @@ The demo projects include small runnable examples and short explainers.
|
||||
- [`Google Sheets API`](./gsheet)
|
||||
- [`ExtendScript for Adobe Apps`](./extendscript)
|
||||
- [`NetSuite SuiteScript`](./netsuite)
|
||||
- [`SalesForce Lightning Web Components`](./salesforce)
|
||||
- [`Excel JavaScript API`](./excel)
|
||||
- [`Headless Browsers`](https://github.com/SheetJS/SheetJS/tree/master/demos/headless/)
|
||||
- [`Other JavaScript Engines`](https://github.com/SheetJS/SheetJS/tree/master/demos/altjs/)
|
||||
|
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 27 KiB |
@ -54,6 +54,9 @@ The [demos](../getting-started/demos) cover special deployments in more detail.
|
||||
`XLSX.readFile` supports reading local files in platforms like NodeJS. In other
|
||||
platforms like React Native, `XLSX.read` should be called with file data.
|
||||
|
||||
In-browser processing where users drag-and-drop files or use a file element are
|
||||
covered in [the "User Submissions" example.](#example-user-submissions)
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="nodejs" label="NodeJS">
|
||||
|
||||
@ -180,16 +183,33 @@ The [`extendscript` demo](../getting-started/demos/extendscript) includes a more
|
||||
|
||||
### Example: User Submissions
|
||||
|
||||
<details>
|
||||
<summary><b>User-submitted file in a web page ("Drag-and-Drop")</b> (click to show)</summary>
|
||||
This example focuses on user-submitted files through a drag-and-drop event, HTML
|
||||
file input element, or network request.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="browser" label="Browser">
|
||||
|
||||
**For modern websites targeting Chrome 76+**, `File#arrayBuffer` is recommended:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="dnd" label="Drag and Drop">
|
||||
|
||||
Assume `drop_dom_element` is the DOM element that will listen for changes:
|
||||
|
||||
```html
|
||||
<div id="drop_dom_element">Drop files here</div>
|
||||
```
|
||||
|
||||
The event property is `e.dataTransfer`. The code snippet highlights the
|
||||
difference between the drag-and-drop example and the file input example:
|
||||
|
||||
For modern websites targeting Chrome 76+, `File#arrayBuffer` is recommended:
|
||||
|
||||
```js
|
||||
// XLSX is a global from the standalone script
|
||||
|
||||
async function handleDropAsync(e) {
|
||||
e.stopPropagation(); e.preventDefault();
|
||||
// highlight-next-line
|
||||
const f = e.dataTransfer.files[0];
|
||||
/* f is a File */
|
||||
const data = await f.arrayBuffer();
|
||||
@ -201,11 +221,57 @@ async function handleDropAsync(e) {
|
||||
drop_dom_element.addEventListener("drop", handleDropAsync, false);
|
||||
```
|
||||
|
||||
For maximal compatibility, the `FileReader` API should be used:
|
||||
</TabItem>
|
||||
<TabItem value="file" label="HTML File Input Element">
|
||||
|
||||
Starting with an HTML INPUT element with `type="file"`:
|
||||
|
||||
```html
|
||||
<input type="file" id="input_dom_element">
|
||||
```
|
||||
|
||||
The event property is `e.target`. The code snippet highlights the difference
|
||||
between the drag-and-drop example and the file input example:
|
||||
|
||||
```js
|
||||
// XLSX is a global from the standalone script
|
||||
|
||||
async function handleFileAsync(e) {
|
||||
// highlight-next-line
|
||||
const file = e.target.files[0];
|
||||
const data = await file.arrayBuffer();
|
||||
/* data is an ArrayBuffer */
|
||||
const workbook = XLSX.read(data);
|
||||
|
||||
/* DO SOMETHING WITH workbook HERE */
|
||||
}
|
||||
input_dom_element.addEventListener("change", handleFileAsync, false);
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
<https://oss.sheetjs.com/sheetjs/> demonstrates the FileReader technique.
|
||||
|
||||
|
||||
**For maximal compatibility (IE10+)**, the `FileReader` approach is recommended:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="dnd" label="Drag and Drop">
|
||||
|
||||
Assume `drop_dom_element` is the DOM element that will listen for changes:
|
||||
|
||||
```html
|
||||
<div id="drop_dom_element">Drop files here</div>
|
||||
```
|
||||
|
||||
The event property is `e.dataTransfer`. The code snippet highlights the
|
||||
difference between the drag-and-drop example and the file input example:
|
||||
|
||||
```js
|
||||
function handleDrop(e) {
|
||||
e.stopPropagation(); e.preventDefault();
|
||||
// highlight-next-line
|
||||
var f = e.dataTransfer.files[0];
|
||||
/* f is a File */
|
||||
var reader = new FileReader();
|
||||
@ -221,12 +287,8 @@ function handleDrop(e) {
|
||||
drop_dom_element.addEventListener("drop", handleDrop, false);
|
||||
```
|
||||
|
||||
<https://oss.sheetjs.com/sheetjs/> demonstrates the FileReader technique.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>User-submitted file with an HTML INPUT element</b> (click to show)</summary>
|
||||
</TabItem>
|
||||
<TabItem value="file" label="HTML File Input Element">
|
||||
|
||||
Starting with an HTML INPUT element with `type="file"`:
|
||||
|
||||
@ -234,26 +296,12 @@ Starting with an HTML INPUT element with `type="file"`:
|
||||
<input type="file" id="input_dom_element">
|
||||
```
|
||||
|
||||
For modern websites targeting Chrome 76+, `Blob#arrayBuffer` is recommended:
|
||||
|
||||
```js
|
||||
// XLSX is a global from the standalone script
|
||||
|
||||
async function handleFileAsync(e) {
|
||||
const file = e.target.files[0];
|
||||
const data = await file.arrayBuffer();
|
||||
/* data is an ArrayBuffer */
|
||||
const workbook = XLSX.read(data);
|
||||
|
||||
/* DO SOMETHING WITH workbook HERE */
|
||||
}
|
||||
input_dom_element.addEventListener("change", handleFileAsync, false);
|
||||
```
|
||||
|
||||
For broader support (including IE10+), the `FileReader` approach is recommended:
|
||||
The event property is `e.target`. The code snippet highlights the difference
|
||||
between the drag-and-drop example and the file input example:
|
||||
|
||||
```js
|
||||
function handleFile(e) {
|
||||
// highlight-next-line
|
||||
var file = e.target.files[0];
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
@ -268,13 +316,13 @@ function handleFile(e) {
|
||||
input_dom_element.addEventListener("change", handleFile, false);
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The [`oldie` demo](https://github.com/SheetJS/SheetJS/tree/master/demos/oldie/) shows an IE-compatible fallback scenario.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary><b>NodeJS Server File Uploads</b> (click to show)</summary>
|
||||
</TabItem>
|
||||
<TabItem value="nodejs" label="NodeJS">
|
||||
|
||||
`read` can accept a NodeJS buffer. `readFile` can read files generated by a
|
||||
HTTP POST request body parser like [`formidable`](https://npm.im/formidable):
|
||||
@ -299,7 +347,64 @@ const server = http.createServer((req, res) => {
|
||||
|
||||
The [`server` demo](https://github.com/SheetJS/SheetJS/tree/master/demos/server) has more advanced examples.
|
||||
|
||||
</details>
|
||||
</TabItem>
|
||||
<TabItem value="deno" label="Deno">
|
||||
|
||||
[Drash](https://drash.land/drash/) is a framework for Deno's HTTP server. In a
|
||||
`POST` request handler, the body parser can pull file data into a `Uint8Array`:
|
||||
|
||||
<pre><code parentName="pre" {...{"className": "language-ts"}}>{`\
|
||||
// @deno-types="https://cdn.sheetjs.com/xlsx-${current}/package/types/index.d.ts"
|
||||
import * as XLSX from 'https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs';
|
||||
/* load the codepage support library for extended support with older formats */
|
||||
import * as cptable from 'https://cdn.sheetjs.com/xlsx-${current}/package/dist/cpexcel.full.mjs';
|
||||
XLSX.set_cptable(cptable);
|
||||
|
||||
import * as Drash from "https://deno.land/x/drash@v2.5.4/mod.ts";
|
||||
|
||||
class SheetResource extends Drash.Resource {
|
||||
public paths = ["/"];
|
||||
|
||||
public POST(request: Drash.Request, response: Drash.Response) {
|
||||
// highlight-next-line
|
||||
const file = request.bodyParam<Drash.Types.BodyFile>("file");
|
||||
if (!file) throw new Error("File is required!");
|
||||
// highlight-next-line
|
||||
var wb = XLSX.read(file.content, {type: "buffer"});
|
||||
var html = XLSX.utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
|
||||
return response.html(html);
|
||||
}
|
||||
}
|
||||
|
||||
const server = new Drash.Server({ hostname: "", port: 7262, protocol: "http",
|
||||
resources: [
|
||||
// highlight-next-line
|
||||
SheetResource,
|
||||
],
|
||||
});
|
||||
|
||||
server.run();`}</code></pre>
|
||||
|
||||
:::note
|
||||
|
||||
Deno must be run with the `--allow-net` flag to enable network requests:
|
||||
|
||||
```bash
|
||||
$ deno run --allow-net test-server.ts
|
||||
```
|
||||
|
||||
To test, submit a POST request to http://localhost:7262 including a file:
|
||||
|
||||
```bash
|
||||
curl -X POST -F "file=@test.xlsx" http://localhost:7262/
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
### Example: Remote File
|
||||
|
||||
@ -355,6 +460,7 @@ Node 17.5 and 18.0 have native support for fetch:
|
||||
```js
|
||||
const XLSX = require("xlsx");
|
||||
|
||||
const url = "http://oss.sheetjs.com/test_files/formula_stress_test.xlsx";
|
||||
const data = await (await fetch(url)).arrayBuffer();
|
||||
/* data is an ArrayBuffer */
|
||||
const workbook = XLSX.read(data);
|
||||
@ -368,6 +474,7 @@ For broader compatibility, third-party modules are recommended.
|
||||
var XLSX = require("xlsx");
|
||||
var request = require("request");
|
||||
|
||||
var url = "http://oss.sheetjs.com/test_files/formula_stress_test.xlsx";
|
||||
request({url: url, encoding: null}, function(err, resp, body) {
|
||||
var workbook = XLSX.read(body);
|
||||
|
||||
@ -381,6 +488,7 @@ request({url: url, encoding: null}, function(err, resp, body) {
|
||||
const XLSX = require("xlsx");
|
||||
const axios = require("axios");
|
||||
|
||||
const url = "http://oss.sheetjs.com/test_files/formula_stress_test.xlsx";
|
||||
(async() => {
|
||||
const res = await axios.get(url, {responseType: "arraybuffer"});
|
||||
/* res.data is a Buffer */
|
||||
@ -390,6 +498,34 @@ const axios = require("axios");
|
||||
})();
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="deno" label="Deno">
|
||||
|
||||
Deno has native support for fetch.
|
||||
|
||||
<pre><code parentName="pre" {...{"className": "language-ts"}}>{`\
|
||||
// @deno-types="https://cdn.sheetjs.com/xlsx-${current}/package/types/index.d.ts"
|
||||
import * as XLSX from 'https://cdn.sheetjs.com/xlsx-${current}/package/xlsx.mjs';
|
||||
/* load the codepage support library for extended support with older formats */
|
||||
import * as cptable from 'https://cdn.sheetjs.com/xlsx-${current}/package/dist/cpexcel.full.mjs';
|
||||
XLSX.set_cptable(cptable);
|
||||
|
||||
const url = "http://oss.sheetjs.com/test_files/formula_stress_test.xlsx";
|
||||
// highlight-next-line
|
||||
const data = await (await fetch(url)).arrayBuffer();
|
||||
/* data is an ArrayBuffer */
|
||||
const workbook = XLSX.read(data);`}</code></pre>
|
||||
|
||||
:::note
|
||||
|
||||
Deno must be run with the `--allow-net` flag to enable network requests:
|
||||
|
||||
```
|
||||
$ deno run --allow-net test-fetch.ts
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="electron" label="Electron">
|
||||
|
||||
@ -400,6 +536,7 @@ resources. Responses should be manually concatenated using `Buffer.concat`:
|
||||
const XLSX = require("xlsx");
|
||||
const { net } = require("electron");
|
||||
|
||||
const url = "http://oss.sheetjs.com/test_files/formula_stress_test.xlsx";
|
||||
const req = net.request(url);
|
||||
req.on("response", (res) => {
|
||||
const bufs = []; // this array will collect all of the buffers
|
||||
|
@ -62,9 +62,6 @@ rest of the worksheet structure.
|
||||
|
||||
#### Examples
|
||||
|
||||
<details>
|
||||
<summary><b>Add a new worksheet to a workbook</b> (click to show)</summary>
|
||||
|
||||
This example uses [`XLSX.utils.aoa_to_sheet`](../api/utilities#array-of-arrays-input).
|
||||
|
||||
```js
|
||||
@ -81,8 +78,6 @@ var ws = XLSX.utils.aoa_to_sheet(ws_data);
|
||||
XLSX.utils.book_append_sheet(wb, ws, ws_name);
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Modifying Cell Values
|
||||
|
||||
#### API
|
||||
@ -117,9 +112,6 @@ function and the optional `opts` argument in more detail.
|
||||
|
||||
#### Examples
|
||||
|
||||
<details>
|
||||
<summary><b>Appending rows to a worksheet</b> (click to show)</summary>
|
||||
|
||||
The special origin value `-1` instructs `sheet_add_aoa` to start in column A of
|
||||
the row after the last row in the range, appending the data:
|
||||
|
||||
@ -130,8 +122,6 @@ XLSX.utils.sheet_add_aoa(worksheet, [
|
||||
], { origin: -1 });
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Modifying Other Worksheet / Workbook / Cell Properties
|
||||
|
||||
The ["Common Spreadsheet Format"](../csf/general) section describes
|
||||
|
@ -1,10 +1,24 @@
|
||||
# Spreadsheet Features
|
||||
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
|
||||
|
||||
Even for basic features like date storage, the official Excel formats store the
|
||||
same content in different ways. The parsers are expected to convert from the
|
||||
underlying file format representation to the Common Spreadsheet Format. Writers
|
||||
are expected to convert from CSF back to the underlying file format.
|
||||
|
||||
The following topics are covered in sub-pages:
|
||||
|
||||
<ul>{useCurrentSidebarCategory().items.map((item, index) => {
|
||||
const listyle = (item.customProps?.icon) ? {
|
||||
listStyleImage: `url("${item.customProps.icon}")`
|
||||
} : {};
|
||||
return (<li style={listyle} {...(item.customProps?.class ? {className: item.customProps.class}: {})}>
|
||||
<a href={item.href}>{item.label}</a>{item.customProps?.summary && (" - " + item.customProps.summary)}
|
||||
</li>);
|
||||
})}</ul>
|
||||
|
||||
## Row and Column Properties
|
||||
|
||||
<details>
|
||||
@ -133,12 +147,7 @@ The format can either be specified as a string or as an index into the format
|
||||
table. Parsers are expected to populate `workbook.SSF` with the number format
|
||||
table. Writers are expected to serialize the table.
|
||||
|
||||
Custom tools should ensure that the local table has each used format string
|
||||
somewhere in the table. Excel convention mandates that the custom formats start
|
||||
at index 164. The following example creates a custom format from scratch:
|
||||
|
||||
<details>
|
||||
<summary><b>New worksheet with custom format</b> (click to show)</summary>
|
||||
The following example creates a custom format from scratch:
|
||||
|
||||
```js
|
||||
var wb = {
|
||||
@ -154,8 +163,6 @@ var wb = {
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
The rules are slightly different from how Excel displays custom number formats.
|
||||
In particular, literal characters must be wrapped in double quotes or preceded
|
||||
by a backslash. For more info, see the Excel documentation article
|
||||
@ -371,32 +378,51 @@ supported in `XLSM`, `XLSB`, and `BIFF8 XLS` formats. The supported format
|
||||
writers automatically insert the data blobs if it is present in the workbook and
|
||||
associate with the worksheet names.
|
||||
|
||||
<details>
|
||||
<summary><b>Custom Code Names</b> (click to show)</summary>
|
||||
The `vbaraw` property stores raw bytes. [SheetJS Pro](https://sheetjs.com/pro)
|
||||
offers a special component for extracting macro text from the VBA blob, editing
|
||||
the VBA project, and exporting new VBA blobs.
|
||||
|
||||
#### Round-tripping Macro Enabled Files
|
||||
|
||||
In order to preserve macro when reading and writing files, the `bookVBA` option
|
||||
must be set to true when reading and when writing. In addition, the output file
|
||||
format must support macros. `XLSX` notably does not support macros, and `XLSM`
|
||||
should be used in its place:
|
||||
|
||||
```js
|
||||
/* Reading data */
|
||||
var wb = XLSX.read(data, { bookVBA: true }); // read file and distill VBA blob
|
||||
var vbablob = wb.vbaraw;
|
||||
```
|
||||
|
||||
#### Code Names
|
||||
|
||||
By default, Excel will use `ThisWorkbook` or a translation `DieseArbeitsmappe`
|
||||
for the workbook. Each worksheet will be identified using the default `Sheet#`
|
||||
naming pattern even if the worksheet names have changed.
|
||||
|
||||
A custom workbook code name will be stored in `wb.Workbook.WBProps.CodeName`.
|
||||
For exports, assigning the property will override the default value.
|
||||
|
||||
The workbook code name is stored in `wb.Workbook.WBProps.CodeName`. By default,
|
||||
Excel will write `ThisWorkbook` or a translated phrase like `DieseArbeitsmappe`.
|
||||
Worksheet and Chartsheet code names are in the worksheet properties object at
|
||||
`wb.Workbook.Sheets[i].CodeName`. Macrosheets and Dialogsheets are ignored.
|
||||
|
||||
The readers and writers preserve the code names, but they have to be manually
|
||||
set when adding a VBA blob to a different workbook.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Macrosheets</b> (click to show)</summary>
|
||||
#### Macrosheets
|
||||
|
||||
Older versions of Excel also supported a non-VBA "macrosheet" sheet type that
|
||||
stored automation commands. These are exposed in objects with the `!type`
|
||||
property set to `"macro"`.
|
||||
|
||||
</details>
|
||||
Under the hood, Excel treats Macrosheets as normal worksheets with special
|
||||
interpretation of the function expressions.
|
||||
|
||||
<details>
|
||||
<summary><b>Detecting macros in workbooks</b> (click to show)</summary>
|
||||
#### Detecting Macros in Workbooks
|
||||
|
||||
The `vbaraw` field will only be set if macros are present, so testing is simple:
|
||||
The `vbaraw` field will only be set if macros are present. Macrosheets will be
|
||||
explicitly flagged. Combining the two checks yields a simple function:
|
||||
|
||||
```js
|
||||
function wb_has_macro(wb/*:workbook*/)/*:boolean*/ {
|
||||
@ -406,5 +432,3 @@ function wb_has_macro(wb/*:workbook*/)/*:boolean*/ {
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
BIN
docz/static/files/sfcustcomp.png
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
docz/static/files/sfexport.png
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
docz/static/files/sfinitial.png
Normal file
After Width: | Height: | Size: 98 KiB |
BIN
docz/static/files/sfstatic.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
docz/static/files/sfversion.png
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
docz/static/files/sfxlexport.png
Normal file
After Width: | Height: | Size: 82 KiB |