docs.sheetjs.com/docz/docs/03-demos/06-desktop/03-wails.md
2023-06-25 14:44:00 -04:00

11 KiB

title sidebar_label description pagination_prev pagination_next sidebar_position sidebar_custom_props
Wails Wails Build data-intensive desktop apps using Wails. Seamlessly integrate spreadsheets into your app using SheetJS. Modernize Excel-powered business processes with confidence. demos/mobile/index demos/data/index 3
summary
Webview + Go Backend

Spreadsheet-Powered Wails Apps

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

Wails is a modern toolkit for building desktop apps. Wails apps pair a Go-powered backend with a JavaScript-powered frontend1.

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

This demo uses Wails and SheetJS to pull data from a spreadsheet and display the data in the app. We'll explore how to load SheetJS in a Wails app and exchange file data between the JavaScript frontend and Go backend.

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

Windows macOS Linux

Win10 screenshot

macOS screenshot

Linux screenshot

:::info

This demo assumes some familiarity with JavaScript and with Go. If you would prefer a pure JavaScript solution, the Electron platform provides many native features out of the box.

:::

Integration Details

The SheetJS NodeJS Module can be installed in the frontend folder and imported in frontend scripts.

:::caution

Wails currently does not provide the equivalent of NodeJS fs module.

Reading and writing raw file data must be implemented in native Go code.

:::

This demo includes native Go code for showing dialogs and reading and writing files. When sending data between Go and JavaScript code, the raw files are encoded as Base64 strings.

Reading Files

When the user clicks the "Import File" button, the frontend tells the Go backend to read data. The user will be presented with a file picker to select a file to read. The Go backend will read the data, encode as a Base64 string, and send the result to the frontend.

The frontend will parse the data using the SheetJS read method2, generate HTML tables with sheet_to_html3, and display the tables on the frontend.

The following diagram summarizes the steps:

sequenceDiagram
  actor User
  participant JS as Frontend (JS)
  participant Go as Backend (Go)
  User->>JS: click button
  JS->>Go: ask for data
  Note over Go: Show Open Dialog
  Note over Go: Read File Bytes
  Note over Go: Generate Base64
  Go->>JS: return data
  Note over JS: Parse Data<br/>`read`
  Note over JS: Display Table<br/>`sheet_to_html`
  JS->>User: app shows data

Go

The Wails runtime provides the cross-platform OpenFileDialog function4 to show a file picker. The Go standard library provides methods for reading data from the selected file5 and encoding in a Base64 string6

import (
  "context"
// highlight-start
  "encoding/base64"
  "os"
  "github.com/wailsapp/wails/v2/pkg/runtime"
// highlight-end
)

type App struct {
  ctx context.Context
}

// ReadFile shows an open file dialog and returns the data as Base64 string
func (a *App) ReadFile() string {
  // highlight-next-line
  selection, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
    Title: "Select File",
    Filters: []runtime.FileFilter{
      { DisplayName: "Excel Workbooks (*.xlsx)", Pattern: "*.xlsx", },
      // ... more filters for more file types
    },
  })
  if err != nil { return "" } // The demo app shows an error message
  // highlight-next-line
  data, err := os.ReadFile(selection)
  if err != nil { return "" } // The demo app shows an error message
  // highlight-next-line
  return base64.StdEncoding.EncodeToString(data)
}

JS

Wails will automatically create bindings for use in JS. The App binding module will export the function ReadFile.

import { read, utils } from 'xlsx';
import { ReadFile } from '../wailsjs/go/main/App';

async function importFile(evt) {
// highlight-start
  /* call the native Go function and receive a base64 string */
  const b64 = await ReadFile();
  /* parse the base64 string with SheetJS */
  const wb = read(b64, { type: "base64" });
// highlight-end

  const ws = wb.Sheets[wb.SheetNames[0]]; // get the first worksheet
  return utils.sheet_to_html(ws); // generate HTML table
}

Writing Files

:::info

The SheetJS write method7 can write spreadsheets in a number of formats8 including XLSX, XLSB, XLS, and NUMBERS. It expects a bookType option. This means the frontend needs to know the output file name before creating the file.

:::

When the user clicks the "Export File" button, the frontend asks the Go backend for the output filename and path. The user will be presented with a file picker to select the output folder and workbook type. The backend will send the name to the frontend.

The frontend will generate a workbook object from the table using the SheetJS table_to_book method9. The SheetJS write method10 will generate a Base64 string from the data.

The frontend will send the Base64 string to the backend. The backend will write the data to a file in the selected folder.

sequenceDiagram
  actor User
  participant JS as Frontend (JS)
  participant Go as Backend (Go)
  User->>JS: click button
  JS->>Go: ask for path
  Note over Go: Show Save Dialog
  Go->>JS: path to save file
  Note over JS: Read from Table<br/>`table_to_book`
  Note over JS: Write Workbook<br/>`write`
  JS->>Go: base64-encoded bytes
  Note over Go: Decode Data
  Note over Go: Write to File
  Go->>JS: write finished
  JS->>User: alert
Go

Two Go functions will be exposed.

  • SaveFile will show the file picker and return the path. It will use the cross-platform SaveFileDialog function11.
import (
  "context"
// highlight-next-line
  "github.com/wailsapp/wails/v2/pkg/runtime"
)

type App struct {
  ctx context.Context
}

func (a *App) SaveFile() string {
// highlight-next-line
  selection, err := runtime.SaveFileDialog(a.ctx, runtime.SaveDialogOptions{
    Title: "Select File",
    DefaultFilename: "SheetJSWails.xlsx",
    Filters: []runtime.FileFilter{
      { DisplayName: "Excel Workbooks (*.xlsx)", Pattern: "*.xlsx", },
      // ... more filters for more file types
    },
  })
  if err != nil { return "" } // The demo app shows an error message
  return selection
}
  • WriteFile performs the file write given a Base64 string and file path. The Go standard library provides methods for decoding Base64 strings12 and writing data to the filesystem13
import (
  "context"
// highlight-start
  "encoding/base64"
  "os"
// highlight-end
)

type App struct {
  ctx context.Context
}

func (a *App) WriteFile(b64 string, path string) {
  // highlight-start
  buf, _ := base64.StdEncoding.DecodeString(b64);
  _ = os.WriteFile(path, buf, 0644);
  // highlight-end
}

JS

Wails will automatically create bindings for use in JS. The App binding module will export the functions SaveFile and WriteFile:

import { utils, write } from 'xlsx';
import { SaveFile, WriteFile } from '../wailsjs/go/main/App';

async function exportFile(table_element) {
  /* generate workbook */
  const wb = utils.table_to_book(table_element);

  /* show save picker and get path */
  const path = await SaveFile();

  /* get the file extension -> bookType */
  const bookType = path.slice(path.lastIndexOf(".")+1);

  /* generate base64 string */
  const b64 = write(wb, { bookType: bookType, type: "base64" });

  /* write to file */
  await WriteFile(b64, path);
}

Complete Example

:::note

This demo was tested against Wails v2.4.1 on 2023 April 30 using the Svelte TypeScript starter.

:::

  1. Read the Wails "Getting Started" guide14 and install dependencies.
Installation Notes (click to show)

Wails will require:

  • A recent version of Go.
  • The "LTS" version of NodeJS.

After installing both, run the following command to install Wails:

go install github.com/wailsapp/wails/v2/cmd/wails@latest

Once that finishes, run the following command in a new terminal window:

wails doctor

The output will include a # Diagnosis section. It should display:

# Diagnosis

Your system is ready for Wails development!

If a required dependency is missing, it will be displayed.

:::note

None of the optional packages are required for building and running this demo.

:::

  1. Create a new Wails app:
wails init -n sheetjs-wails -t svelte-ts
  1. Enter the directory:
cd sheetjs-wails
  1. Install front-end dependencies:

{\ cd frontend curl -L -o src/assets/logo.png https://sheetjs.com/sketch1024.png npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz cd ..}

  1. Download source files:
  • Download app.go and replace app.go
  • Download App.svelte and replace frontend/src/App.svelte
curl -L -o app.go https://docs.sheetjs.com/wails/app.go
curl -L -o frontend/src/App.svelte https://docs.sheetjs.com/wails/App.svelte
  1. Build the app with
wails build

At the end, it will print the path to the generated program. Run the program!


  1. See "How does it Work?" in the Wails documentation. ↩︎

  2. See read in "Reading Files" ↩︎

  3. See sheet_to_html in "Utilities" ↩︎

  4. See OpenFileDialog in the Wails documentation. ↩︎

  5. See ReadFile in the Go documentation ↩︎

  6. See EncodeToString in the Go documentation ↩︎

  7. See write in "Writing Files" ↩︎

  8. See "Supported Output Formats" type in "Writing Files" ↩︎

  9. See "HTML Table Input" in "Utilities" ↩︎

  10. See write in "Writing Files" ↩︎

  11. See SaveFileDialog in the Wails documentation. ↩︎

  12. See DecodeString in the Go documentation ↩︎

  13. See WriteFile in the Go documentation ↩︎

  14. See "Installation" in the Wails documentation. ↩︎