docs.sheetjs.com/docz/docs/03-demos/06-desktop/05-neutralino.md
2023-06-29 17:07:52 -04:00

11 KiB
Raw Blame History

title sidebar_label description pagination_prev pagination_next sidebar_position sidebar_custom_props
NeutralinoJS NeutralinoJS Build data-intensive desktop apps using NeutralinoJS. Seamlessly integrate spreadsheets into your app using SheetJS. Quickly modernize Excel-powered business processes. demos/mobile/index demos/data/index 5
summary
Webview + Lightweight Extensions

Data Munging in NeutralinoJS

import current from '/version.js'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock';

NeutralinoJS is a modern desktop app framework. NeutralinoJS apps pair platform-native browser tools with a static web server.

SheetJS is a JavaScript library for reading and writing data from spreadsheets.

This demo uses NeutralinoJS and SheetJS to pull data from a spreadsheet and display the data in the app. We'll explore how to load SheetJS in a NeutralinoJS app and use native features to read and write files.

The "Complete Example" section covers a complete desktop app to read and write workbooks. The app will look like the screenshots below:

Win10 macOS Linux

Win10 screenshot

macOS screenshot

Linux screenshot

Integration Details

The SheetJS Standalone build can be added to the entry index.html

For code running in the window, native methods must be explicitly enabled in the NeutralinoJS neutralino.conf.json settings file1.

  • os.* enables the open and save dialog methods.
  • filesystem.* enables reading and writing file data.

The starter app enables os.* so typically one line must be added:

  "nativeAllowList": [
    "app.*",
    "os.*",
// highlight-next-line
    "filesystem.*",
    "debug.log"
  ],

Reading Files

There are three steps to reading files:

  1. Show an open file dialog with Neutralino.os.showOpenDialog2. This method resolves to the selected path.

  2. Read raw data from the file with Neutralino.filesystem.readBinaryFile3. This method resolves to a standard ArrayBuffer.

  3. Parse the data with the SheetJS read method4. This method returns a SheetJS workbook object.

The following code example defines a single function openFile that performs all three steps and returns a SheetJS workbook object:

const filters = [
  {name: "Excel Binary Workbook", extensions: ["xls", "xlsb"]},
  {name: "Excel Workbook", extensions: ["xls", "xlsx"]},
]

async function openFile() {
  /* show open file dialog */
  const [filename] = await Neutralino.os.showOpenDialog(
    'Open a spreadsheet',
    { filters, multiSelections: false }
  );

  /* read data into an ArrayBuffer */
  const ab = await Neutralino.filesystem.readBinaryFile(filename);

  /* parse with SheetJS */
  const wb = XLSX.read(ab);
  return wb;
}

At this point, standard SheetJS utility functions5 can extract data from the workbook object. The demo includes a button that calls sheet_to_html6 to generate an HTML TABLE and add to the DOM:

const open_button_callback = async() => {
  const wb = await openFile();

  /* get the first worksheet */
  // highlight-start
  const ws = wb.Sheets[wb.SheetNames[0]];
  // highlight-end

  /* get data from the first worksheet */
  // highlight-start
  const html = XLSX.utils.sheet_to_html(ws);
  // highlight-end

  /* display table */
  document.getElementById('info').innerHTML = html;
};

Writing Files

There are three steps to reading files:

  1. Show a file dialog with Neutralino.os.showSaveDialog7. This method resolves to the selected path.

  2. Write the data with the SheetJS write method8. The output book type can be inferred from the selected file path. Using the buffer output type9, the method returns a Uint8Array object that plays nice with NeutralinoJS.

  3. Write to file with Neutralino.filesystem.writeBinaryFile10.

The following code example defines a single function saveFile that performs all three steps starting from a SheetJS workbook object:

const filters = [
  {name: "Excel Binary Workbook", extensions: ["xls", "xlsb"]},
  {name: "Excel Workbook", extensions: ["xls", "xlsx"]},
]

async function saveFile(wb) {
  /* show save file dialog */
  const filename = await Neutralino.os.showSaveDialog(
    'Save to file',
    { filters }
  );

  /* Generate workbook */
  const bookType = filename.slice(filename.lastIndexOf(".") + 1);
  const data = XLSX.write(wb, { bookType, type: "buffer" });

  /* save data to file */
  await Neutralino.filesystem.writeBinaryFile(filename, data);
}

The demo includes a button that calls table_to_book11 to generate a workbook object from the HTML table:

const save_button_callback = async() => {
  /* get the table */
  const tbl = document.getElementById('info').querySelector('table');

  /* generate workbook from the table */
  // highlight-start
  const wb = XLSX.utils.table_to_book(tbl);
  // highlight-end

  await saveFile(wb);
}

Complete Example

:::note pass

This demo was tested in the following environments:

OS and Version Arch Server Client Date
macOS 13.4.1 x64 v4.10.0 v3.8.2 2023-06-28
macOS 13.4.1 ARM v4.10.0 v3.8.2 2023-06-28
Windows 10 x64 v4.7.0 v3.6.0 2023-03-19
Linux (HoloOS) x64 v4.7.0 v3.6.0

:::

The app core state will be the HTML table. Reading files will add the table to the window. Writing files will parse the table into a spreadsheet.

  1. Create a new NeutralinoJS app:
npx @neutralinojs/neu create sheetjs-neu
cd sheetjs-neu
  1. Download Standalone build and place in the resources/js/ folder:

{\ curl -L -o resources/js/xlsx.full.min.js https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js}

  1. Add the highlighted line to neutralino.config.json in nativeAllowList:
  "nativeAllowList": [
    "app.*",
    "os.*",
// highlight-start
    "filesystem.*",
// highlight-end
    "debug.log"
  ],
  1. Set up skeleton app and print version info:
  • Replace the contents of resources/index.html with the following code:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>SheetJS + NeutralinoJS</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <div id="neutralinoapp">
      <h1>SheetJS × NeutralinoJS</h1>
      <button onclick="importData()">Import Data</button>
      <button onclick="exportData()">Export Data</button>
      <div id="info"></div>
    </div>
    <script src="js/neutralino.js"></script>
    <!-- Load the browser build and make XLSX available to main.js -->
    <script src="js/xlsx.full.min.js"></script>
    <script src="js/main.js"></script>
  </body>
</html>
  • Append the following code to resources/styles.css to center the table:
#info {
    width:100%;
    text-align: unset;
}
table {
    margin: 0 auto;
}
  • Print the version number in the showInfo method of resources/js/main.js:
function showInfo() {
    document.getElementById('info').innerHTML = `
        ${NL_APPID} is running on port ${NL_PORT}  inside ${NL_OS}
        <br/><br/>
        <span>server: v${NL_VERSION} . client: v${NL_CVERSION}</span>
// highlight-start
        <br/><br/>
        <span>SheetJS version ${XLSX.version}</span>
// highlight-end
        `;
}
  1. Run the app:
npx @neutralinojs/neu run

The app should print SheetJS Version {current}

  1. Add the following code to the bottom of resources/js/main.js:
(async() => {
  const ab = await (await fetch("https://sheetjs.com/pres.numbers")).arrayBuffer();
  const wb = XLSX.read(ab);
  const ws = wb.Sheets[wb.SheetNames[0]];
  document.getElementById('info').innerHTML = XLSX.utils.sheet_to_html(ws);
})();

Save the source file, close the app and re-run with npx @neutralinojs/neu run

When the app loads, a table should show in the main screen.

  1. Add importFile and exportFile to the bottom of resources/js/main.js:
async function importData() {
  /* show open dialog */
  const [filename] = await Neutralino.os.showOpenDialog('Open a spreadsheet');

  /* read data */
  const ab = await Neutralino.filesystem.readBinaryFile(filename);
  const wb = XLSX.read(ab);

  /* make table */
  const ws = wb.Sheets[wb.SheetNames[0]];
  document.getElementById('info').innerHTML = XLSX.utils.sheet_to_html(ws);
}

async function exportData() {
  /* show save dialog */
  const filename = await Neutralino.os.showSaveDialog('Save to file');

  /* make workbook */
  const tbl = document.getElementById('info').querySelector("table");
  const wb = XLSX.utils.table_to_book(tbl);

  /* make file */
  const bookType = filename.slice(filename.lastIndexOf(".") + 1);
  const data = XLSX.write(wb, { bookType, type: "buffer" });
  await Neutralino.filesystem.writeBinaryFile(filename, data);
}

Save the source file, close the app and re-run with npx @neutralinojs/neu run

When the app loads, click the "Import File" button and select a spreadsheet to see the contents.

Click "Export File" and enter SheetJSNeu.xlsx to write a new file.

:::caution pass

When saving the file, the actual file extension must be included. Attempting to save as SheetJSNeu will not automatically add the .xlsx extension!

:::

  1. Build production apps:
npx @neutralinojs/neu build

Platform-specific programs will be created in the dist folder.


  1. See nativeAllowList in the NeutralinoJS documentation ↩︎

  2. See os.showOpenDialog in the NeutralinoJS documentation ↩︎

  3. See filesystem.readBinaryFile in the NeutralinoJS documentation ↩︎

  4. See read in "Reading Files" ↩︎

  5. See "Utility Functions" ↩︎

  6. See "HTML Table Output" in "Utility Functions" ↩︎

  7. See os.showSaveDialog in the NeutralinoJS documentation ↩︎

  8. See write in "Writing Files" ↩︎

  9. See "Supported Output Formats" ↩︎

  10. See filesystem.writeBinaryFile in the NeutralinoJS documentation ↩︎

  11. See "HTML Table Input" in "Utility Functions" ↩︎