519 lines
16 KiB
Markdown
519 lines
16 KiB
Markdown
|
---
|
||
|
title: Sheets in OpenUI5 Sites
|
||
|
sidebar_label: OpenUI5
|
||
|
description: Build enterprise-grade applications with OpenUI5. 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: 10
|
||
|
---
|
||
|
|
||
|
import current from '/version.js';
|
||
|
import Tabs from '@theme/Tabs';
|
||
|
import TabItem from '@theme/TabItem';
|
||
|
import CodeBlock from '@theme/CodeBlock';
|
||
|
|
||
|
[OpenUI5](https://openui5.org/) is a JavaScript framework for building enterprise-ready web applications.
|
||
|
|
||
|
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||
|
data from spreadsheets.
|
||
|
|
||
|
This demo uses OpenUI5 and SheetJS to process and generate a spreadsheets. We'll
|
||
|
explore how to load SheetJS in an OpenUI5 app and handle data binding with the
|
||
|
Model-View-Controller pattern.
|
||
|
|
||
|
## Installation
|
||
|
|
||
|
[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
|
||
|
installation with Yarn and other package managers.
|
||
|
|
||
|
The library should be loaded via script tag in your HTML:
|
||
|
|
||
|
<CodeBlock language="html" value="html">
|
||
|
{`<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>`}
|
||
|
</CodeBlock>
|
||
|
|
||
|
This will expose the `XLSX` global object which contains all the necessary methods.
|
||
|
|
||
|
|
||
|
## Internal State
|
||
|
|
||
|
The various SheetJS APIs work with various data shapes. The preferred state
|
||
|
depends on the application.
|
||
|
|
||
|
### JSON Model
|
||
|
|
||
|
The OpenUI5 JSON Model provides ideal integration for spreadsheet data, enabling direct UI control binding.
|
||
|
|
||
|
#### State
|
||
|
|
||
|
The example [presidents sheet](https://docs.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)
|
||
|
|
||
|
</td><td>
|
||
|
|
||
|
```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 }
|
||
|
]
|
||
|
```
|
||
|
|
||
|
</td></tr></tbody></table>
|
||
|
|
||
|
The OpenUI5 `JSONModel`[^1] is a client-side model implementation that stores data as JSON.
|
||
|
Here's a basic example of initializing a model, with a more complete implementation shown later:
|
||
|
|
||
|
<Tabs groupId="lang">
|
||
|
<TabItem name="JS" value="JavaScript">
|
||
|
|
||
|
```js
|
||
|
sap.ui.define(["sap/ui/model/json/JSONModel"], function (JSONModel) {
|
||
|
const oModel = new JSONModel({ presidents: [] });
|
||
|
});
|
||
|
```
|
||
|
</TabItem>
|
||
|
</Tabs>
|
||
|
|
||
|
|
||
|
#### Updating State
|
||
|
|
||
|
The SheetJS [`read`](/docs/api/parse-options) and [`sheet_to_json`](/docs/api/utilities/array#array-output)
|
||
|
functions simplify state updates. OpenUI5's JSONModel provides powerful two-way data binding
|
||
|
capabilities.
|
||
|
|
||
|
```mermaid
|
||
|
flowchart LR
|
||
|
url[(Remote\nFile)]
|
||
|
ab[(Data\nArrayBuffer)]
|
||
|
wb(SheetJS\nWorkbook)
|
||
|
ws(SheetJS\nWorksheet)
|
||
|
aoo(array of\nobjects)
|
||
|
model((JSON\nModel))
|
||
|
url --> |fetch\n\n| ab
|
||
|
ab --> |read\n\n| wb
|
||
|
wb --> |wb.Sheets\nselect sheet| ws
|
||
|
ws --> |sheet_to_json\n\n| aoo
|
||
|
aoo --> |setProperty\nfrom model| model
|
||
|
```
|
||
|
|
||
|
<Tabs groupId="lang">
|
||
|
<TabItem name="JS" value="JavaScript">
|
||
|
|
||
|
```js
|
||
|
_loadExcelFile: async function () {
|
||
|
/* Download from https://docs.sheetjs.com/pres.xlsx */
|
||
|
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||
|
// highlight-start
|
||
|
/* parse */
|
||
|
const wb = XLSX.read(f); // parse the array buffer
|
||
|
/* generate array of objects from first worksheet */
|
||
|
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
|
||
|
const data = XLSX.utils.sheet_to_json(ws); // generate objects
|
||
|
|
||
|
/* update JSONModel */
|
||
|
this.getView().getModel().setProperty("/presidents", data);
|
||
|
// highlight-end
|
||
|
}
|
||
|
```
|
||
|
|
||
|
</TabItem>
|
||
|
</Tabs>
|
||
|
|
||
|
|
||
|
#### Rendering Data
|
||
|
|
||
|
In OpenUI5, the `Model-View-Controller`[^2] pattern is used to organize code and separate concerns.
|
||
|
The view defines the UI structure, the controller handles the logic, and the model manages the data.
|
||
|
|
||
|
```xml title="Example XML for displayin array of objects"
|
||
|
<mvc:View>
|
||
|
<Page>
|
||
|
<!-- The Table component binds to the presidents array -->
|
||
|
<Table width="300px" items="{/presidents}">
|
||
|
<!-- Column definitions specify the table structure -->
|
||
|
<columns>
|
||
|
<Column>
|
||
|
<header> <Text text="Name" /> </header>
|
||
|
</Column>
|
||
|
<Column>
|
||
|
<header> <Text text="Value" /> </header>
|
||
|
</Column>
|
||
|
</columns>
|
||
|
<!-- ColumnItem template defines how each row should be rendered -->
|
||
|
// highlight-start
|
||
|
<items>
|
||
|
<ColumnItem>
|
||
|
<cells>
|
||
|
<Text text="{Name}" />
|
||
|
<Text text="{Index}" />
|
||
|
</cells>
|
||
|
</ColumnItem>
|
||
|
</items>
|
||
|
// highlight-end
|
||
|
</Table>
|
||
|
</Page>
|
||
|
</mvc:View>
|
||
|
```
|
||
|
|
||
|
|
||
|
#### 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 typically used in event handlers attached to buttons or other elements.
|
||
|
|
||
|
A button press handler can generate a local file when clicked:
|
||
|
|
||
|
```mermaid
|
||
|
flowchart LR
|
||
|
state((oModel\ngetProperty))
|
||
|
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
|
||
|
```
|
||
|
|
||
|
```js
|
||
|
/* get model data and export to XLSX */
|
||
|
onExport: function() {
|
||
|
const data = this.getView().getModel().getProperty("/presidents");
|
||
|
/* generate worksheet from model data */
|
||
|
// highlight-next-line
|
||
|
const ws = XLSX.utils.json_to_sheet(data);
|
||
|
/* create workbook and append worksheet */
|
||
|
const wb = XLSX.utils.book_new();
|
||
|
XLSX.utils.book_append_sheet(wb, ws, "Data");
|
||
|
/* export to XLSX */
|
||
|
XLSX.writeFileXLSX(wb, "SheetJSOpenUI5AoO.xlsx");
|
||
|
}
|
||
|
```
|
||
|
|
||
|
|
||
|
#### Complete Component
|
||
|
|
||
|
This complete component example fetches a test file and displays the contents in a table.
|
||
|
When the export button is clicked, an event handler will export a file:
|
||
|
|
||
|
```xml title="webapp/view/Main.view.xml"
|
||
|
<mvc:View
|
||
|
controllerName="sheetjs.openui5.controller.Main"
|
||
|
displayBlock="true"
|
||
|
xmlns="sap.m"
|
||
|
xmlns:mvc="sap.ui.core.mvc"
|
||
|
xmlns:core="sap.ui.core"
|
||
|
core:require="{formatter: 'sheetjs/openui5/model/formatter'}">
|
||
|
<Page>
|
||
|
<VBox width="auto" alignItems="Start">
|
||
|
<Table width="300px" items="{/presidents}">
|
||
|
<columns>
|
||
|
<Column>
|
||
|
<header>
|
||
|
<Text text="Name" />
|
||
|
</header>
|
||
|
</Column>
|
||
|
<Column>
|
||
|
<header>
|
||
|
<Text text="Value" />
|
||
|
</header>
|
||
|
</Column>
|
||
|
</columns>
|
||
|
<items>
|
||
|
<ColumnListItem>
|
||
|
<cells>
|
||
|
<Text text="{Name}" />
|
||
|
<Text text="{Index}" />
|
||
|
</cells>
|
||
|
</ColumnListItem>
|
||
|
</items>
|
||
|
</Table>
|
||
|
<Button text="Export XLSX" press=".onExport" />
|
||
|
</VBox>
|
||
|
</Page>
|
||
|
</mvc:View>
|
||
|
```
|
||
|
|
||
|
```js title="webapp/controller/Main.controller.js"
|
||
|
sap.ui.define(
|
||
|
["./BaseController", "sap/ui/model/json/JSONModel"],
|
||
|
function (BaseController, JSONModel) {
|
||
|
"use strict";
|
||
|
|
||
|
return BaseController.extend("com.demo.xlsx.controller.Main", {
|
||
|
onInit: function () {
|
||
|
/* initialize model */
|
||
|
const oModel = new JSONModel({
|
||
|
presidents: [],
|
||
|
});
|
||
|
this.getView().setModel(oModel);
|
||
|
|
||
|
/* load data when component is initialized */
|
||
|
this._loadExcelFile();
|
||
|
},
|
||
|
|
||
|
_loadExcelFile: async function () {
|
||
|
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||
|
const wb = XLSX.read(f);
|
||
|
const ws = wb.Sheets[wb.SheetNames[0]];
|
||
|
const data = XLSX.utils.sheet_to_json(ws);
|
||
|
this.getView().getModel().setProperty("/presidents", data);
|
||
|
},
|
||
|
|
||
|
onExport: function () {
|
||
|
const data = this.getView().getModel().getProperty("/presidents");
|
||
|
const ws = XLSX.utils.json_to_sheet(data);
|
||
|
const wb = XLSX.utils.book_new();
|
||
|
XLSX.utils.book_append_sheet(wb, ws, "Data");
|
||
|
XLSX.writeFileXLSX(wb, "SheetJSOpenUI5AoO.xlsx");
|
||
|
},
|
||
|
});
|
||
|
}
|
||
|
);
|
||
|
```
|
||
|
|
||
|
<details open>
|
||
|
<summary><b>How to run the example</b> (click to hide)</summary>
|
||
|
|
||
|
:::note Tested Deployments
|
||
|
|
||
|
This demo was tested in the following environments:
|
||
|
| OpenUI5 | generator-easy-ui5 | Date |
|
||
|
|:--------|---------|---------|
|
||
|
| `1.131.1` | `3.8.1` | 2024-12-24 |
|
||
|
|
||
|
:::
|
||
|
|
||
|
1) Create a new site:
|
||
|
|
||
|
```bash
|
||
|
npm i -g generator-easy-ui5
|
||
|
npx yo easy-ui5 app
|
||
|
```
|
||
|
|
||
|
:::note Use the provided defaults:
|
||
|
|
||
|
- Application id: `sheetjs.openui5`
|
||
|
- Framework: `OpenUI5`
|
||
|
- Version: `1.131.1`
|
||
|
- Author: `SheetJS`
|
||
|
- Create new directory: `Y`
|
||
|
- Initialize git: `N`
|
||
|
|
||
|
:::
|
||
|
|
||
|
|
||
|
2) Install the dependencies and start server:
|
||
|
|
||
|
```bash
|
||
|
cd sheetjs.openui5
|
||
|
npm install
|
||
|
npm start
|
||
|
```
|
||
|
|
||
|
3) Open a web browser and access the displayed URL (`http://localhost:8080`)
|
||
|
|
||
|
4) Add SheetJS to your project by including this script tag in `webapp/index.html`:
|
||
|
|
||
|
<CodeBlock language="html" value="html" title="webapp/index.html">
|
||
|
{`<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>`}
|
||
|
</CodeBlock>
|
||
|
|
||
|
5) Replace `webapp/view/Main.view.xml` with the complete implementation above.
|
||
|
6) Replace `webapp/controller/Main.controller.js` with the complete implementation above.
|
||
|
|
||
|
The page will refresh and show a table with an Export button. Click the button and the page will
|
||
|
attempt to download `SheetJSOpenUI5AoO.xlsx`.
|
||
|
|
||
|
7) Build the site:
|
||
|
|
||
|
```bash
|
||
|
npm run build
|
||
|
```
|
||
|
|
||
|
The generated site will be placed in the `dist` folder.
|
||
|
|
||
|
8) Start a local web server:
|
||
|
|
||
|
```bash
|
||
|
npm run start:dist
|
||
|
```
|
||
|
|
||
|
Access the displayed URL (typically http://localhost:8080) with a web browser and test the page.
|
||
|
|
||
|
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
|
||
|
display the data from the first worksheet in a TABLE. The "Export XLSX" button
|
||
|
will generate a workbook that can be opened in a spreadsheet editor.
|
||
|
|
||
|
</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[^3] well!
|
||
|
|
||
|
The [`sheet_to_html`](/docs/api/utilities/html#html-table-output) function generates HTML that is aware of merges and other worksheet
|
||
|
features. Using OpenUI5's `core:HTML`[^4] control, we can render this HTML directly. During export, we extract the table element from the
|
||
|
rendered HTML and use [`table_to_book`](/docs/api/utilities/html#html-table-input) to create a workbook that maintains all the worksheet
|
||
|
features.
|
||
|
|
||
|
In this example, the component directly renders the HTML table in the model through OpenUI5's `core:HTML`[^4] control. For export, we extract
|
||
|
the inner table from the rendered HTML using `getElementsByTagName("table")[1]`, then pass it to [`table_to_book`](/docs/api/utilities/html#html-table-input)
|
||
|
to create a workbook that preserves all features.
|
||
|
|
||
|
|
||
|
```xml title="webapp/view/Main.view.xml"
|
||
|
<mvc:View
|
||
|
controllerName="sheetjs.openui5.controller.Main"
|
||
|
displayBlock="true"
|
||
|
xmlns="sap.m"
|
||
|
xmlns:mvc="sap.ui.core.mvc"
|
||
|
xmlns:core="sap.ui.core"
|
||
|
xmlns:html="http://www.w3.org/1999/xhtml">
|
||
|
<Page>
|
||
|
<content>
|
||
|
<core:HTML content="{/tableHTML}" />
|
||
|
<Button text="Export XLSX" press=".onExport"/>
|
||
|
</content>
|
||
|
</Page>
|
||
|
</mvc:View>
|
||
|
```
|
||
|
|
||
|
```js title="webapp/controller/Main.controller.js"
|
||
|
sap.ui.define(
|
||
|
[
|
||
|
"sap/ui/core/mvc/Controller",
|
||
|
"sap/ui/model/json/JSONModel"
|
||
|
],
|
||
|
function (Controller, JSONModel) {
|
||
|
"use strict";
|
||
|
|
||
|
return Controller.extend("sheetjs.openui5.controller.Main", {
|
||
|
onInit: function () {
|
||
|
/* the component state is an HTML string */
|
||
|
const oModel = new JSONModel({
|
||
|
tableHTML: "",
|
||
|
});
|
||
|
this.getView().setModel(oModel);
|
||
|
|
||
|
/* load data when component is initialized */
|
||
|
this._loadExcelFile();
|
||
|
},
|
||
|
|
||
|
_loadExcelFile: async function () {
|
||
|
const f = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer();
|
||
|
const wb = XLSX.read(f); // parse the array buffer
|
||
|
const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
|
||
|
const opts = {
|
||
|
header: `<table id="excel-table">`,
|
||
|
footer: `</table>`
|
||
|
};
|
||
|
const tableHTML = XLSX.utils.sheet_to_html(ws, opts); // generate HTML
|
||
|
this.getView().getModel().setProperty("/tableHTML", tableHTML); // update state
|
||
|
},
|
||
|
|
||
|
/* get live table and export the XLSX */
|
||
|
onExport: function () {
|
||
|
const tableHTML = this.getView().getModel().getProperty("/tableHTML"); // get HTML string from the model
|
||
|
const div = document.createElement("div"); // create temporary div to parse HTML
|
||
|
div.innerHTML = tableHTML; // insert HTML into div
|
||
|
|
||
|
const table = div.getElementsByTagName("table")[1]; // get inner table (bypasses outer wrapper)
|
||
|
const wb = XLSX.utils.table_to_book(table); // convert table element to workbook
|
||
|
XLSX.writeFileXLSX(wb, "SheetJSOpenUI5HTML.xlsx");
|
||
|
},
|
||
|
});
|
||
|
}
|
||
|
);
|
||
|
```
|
||
|
|
||
|
<details open>
|
||
|
<summary><b>How to run the example</b> (click to hide)</summary>
|
||
|
|
||
|
:::note Tested Deployments
|
||
|
|
||
|
This demo was tested in the following environments:
|
||
|
| OpenUI5 | generator-easy-ui5 | Date |
|
||
|
|:--------|---------|---------|
|
||
|
| `1.131.1` | `3.8.1` | 2024-12-24 |
|
||
|
|
||
|
:::
|
||
|
|
||
|
1) Create a new site:
|
||
|
|
||
|
```bash
|
||
|
npm i -g generator-easy-ui5
|
||
|
npx yo easy-ui5 app
|
||
|
```
|
||
|
|
||
|
:::note Use the provided defaults:
|
||
|
|
||
|
- Application id: `sheetjs.openui5`
|
||
|
- Framework: `OpenUI5`
|
||
|
- Version: `1.131.1`
|
||
|
- Author: `SheetJS`
|
||
|
- Create new directory: `Y`
|
||
|
- Initialize git: `N`
|
||
|
|
||
|
:::
|
||
|
|
||
|
|
||
|
2) Install the dependencies and start server:
|
||
|
|
||
|
```bash
|
||
|
cd sheetjs.openui5
|
||
|
npm install
|
||
|
npm start
|
||
|
````
|
||
|
|
||
|
3) Open a web browser and access the displayed URL (`http://localhost:8080`)
|
||
|
|
||
|
4) Add SheetJS to your project by including this script tag in `webapp/index.html`:
|
||
|
|
||
|
<CodeBlock language="html" value="html" title="webapp/index.html">
|
||
|
{`<script src="https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js"></script>`}
|
||
|
</CodeBlock>
|
||
|
|
||
|
5) Replace `webapp/view/Main.view.xml` with the complete implementation above.
|
||
|
6) Replace `webapp/controller/Main.controller.js` with the complete implementation above.
|
||
|
|
||
|
The page will refresh and show a table with an Export button. Click the button and the page will
|
||
|
attempt to download `SheetJSOpenUI5HTML.xlsx`.
|
||
|
|
||
|
7) Build the site:
|
||
|
|
||
|
```bash
|
||
|
npm run build
|
||
|
```
|
||
|
|
||
|
The generated site will be placed in the `dist` folder.
|
||
|
|
||
|
8) Start a local web server:
|
||
|
|
||
|
```bash
|
||
|
npm run start:dist
|
||
|
```
|
||
|
|
||
|
Access the displayed URL (typically http://localhost:8080) with a web browser and test the page.
|
||
|
|
||
|
When the page loads, the app will fetch https://docs.sheetjs.com/pres.xlsx and
|
||
|
display the data from the first worksheet in a TABLE. The "Export XLSX" button
|
||
|
will generate a workbook that can be opened in a spreadsheet editor.
|
||
|
|
||
|
</details>
|
||
|
|
||
|
|
||
|
[^1]: See [`JSONModel`](https://sdk.openui5.org/1.38.62/docs/api/symbols/sap.ui.model.json.JSONModel.html) in the OpenUI5 documentation.
|
||
|
[^2]: See OpenUI5's [MVC Documentation](https://sdk.openui5.org/topic/91f233476f4d1014b6dd926db0e91070) for detailed explanation of the pattern implementation.
|
||
|
[^3]: See ["Merged Cells" in "SheetJS Data Model"](/docs/csf/features/merges) for more details.
|
||
|
[^4]: See [`core:HTML`](https://sdk.openui5.org/1.38.62/docs/api/symbols/sap.ui.core.HTML.html) in the OpenUI5 documentation.
|