- Application Logic: This is written in Go.
- Frontend User Interface: This consists of HTML, CSS, and JavaScript.
Upon launching the application, Wails initializes a webview that renders all HTML, CSS, and JavaScript content. The Go methods are accessible via bindings, allowing them to be called directly from JavaScript as if they were native functions. Additionally, Wails features an event-based communication system that enables events to be produced or consumed in either Go or JavaScript while transmitting data seamlessly.
Project Setup
To get started with Wails, follow these steps:
- Install Wails CLI: Open your terminal and install the latest version of the Wails CLI tool using the following command:
bash
go install github.com/wailsapp/wails/v2/cmd/wails@latest
- Generate a New Project: Create a new Wails project by executing:
bash
wails init -n todos_app
This command generates a directory structure where the frontend
directory contains templates for basic HTML/CSS/JS files.
- Set Up Frontend with OJET: Delete the existing
frontend
directory and scaffold a new project using OJET CLI:bashojet create frontend --template=basic --typescript --webpack
- Configure
wails.json
: At the root of your project is thewails.json
file where you can configure project-level settings such as output file names and build commands for the frontend. Update the build, watcher, and server URL properties accordingly.
Wails utilizes Go’s embed
feature to integrate files and folders into the application binary. When building an OJET app, a web
directory is generated that needs to be embedded in the main application. Update the embed path in your main.go
file to include this directory.
- Run Your Application: After completing the setup, you can run your application using:
bash
wails dev
This command builds and serves the OJET app while launching the desktop application. During development, you can debug it just like any web application by right-clicking to inspect elements.
Backend Code (Golang)
In the main.go
file, developers can configure application settings such as height and width while disabling resizing options. The complete list of configuration options is available in Wails documentation.
app
struct is exposed from app.go
, where developers can define methods to invoke from OJET UI.Here’s an example of how to implement basic functionality in app.go
:
package main import ( "context" "encoding/json" "os" "path/filepath" "github.com/wailsapp/wails/v2/pkg/runtime" ) type App struct { ctx context.Context fileName string } func NewApp() *App { return &App{} } func (a *App) startup(ctx context.Context) { a.ctx = ctx userDir, _ := os.UserHomeDir() appDir := filepath.Join(userDir, "todos_app") _ = os.Mkdir(appDir, os.ModePerm) a.fileName = filepath.Join(appDir, "todos.json") runtime.EventsOn(ctx, "saveAll", func(data ...interface{}) { if data != nil && data[0] != nil { todos := []byte(data[0].(string)) os.WriteFile(a.fileName, todos, 0644) } }) } func (a *App) GetAllTodos() []string { var todos []string content, err := os.ReadFile(a.fileName) if err != nil { return nil } err = json.Unmarshal(content, &todos) if err != nil { return nil } return todos }
In this code snippet:
- The
startup
method initializes application settings and registers an event named “saveAll” that writes data to a JSON file. - The
GetAllTodos
method reads from this JSON file and returns all todo items as a slice of strings.
After implementing these methods in Go code, generate bindings for the frontend using:
wails generate module
This command creates both JavaScript and TypeScript type definition files under the frontend/wailsjs
directory.
Frontend Code (OJET)
For the user interface components in OJET:
- An
<oj-input-text>
field allows users to add new todo items. - An
<oj-list-view>
component displays all todos. - Each
<oj-list-item>
shows todo text along with a delete button. - A “Save All” button persists data to the JSON file.
Here’s an example of how this can be structured in appController.ts
:
typescript
import * as ko from 'knockout'; import Context = require('ojs/ojcontext'); import 'ojs/ojbutton'; import { ojButton } from 'ojs/ojbutton'; import 'ojs/ojinputtext'; import 'ojs/ojlistitemlayout'; import 'ojs/ojlistview'; import ArrayDataProvider = require('ojs/ojarraydataprovider'); import * as App from './../../wailsjs/go/main/App'; import * as Runtime from './../../wailsjs/runtime/runtime'; class RootViewModel { newTodo = ko.observable(''); todos = ko.observableArray<string>([]); readonly todosDP = new ArrayDataProvider(this.todos); constructor() { Context.getPageContext().getBusyContext().applicationBootstrapComplete(); this.fetchTodos(); } private fetchTodos = async () => { const data = await App.GetAllTodos(); if (data) this.todos(data); }; handleAddTodo = (_: ojButton.ojAction) => { if (this.newTodo() && this.newTodo().trim()) { this.todos.push(this.newTodo()); this.newTodo(''); } }; handleDeleteTodo = (todo: string) => { const idx = this.todos().findIndex((item) => item === todo); if (idx !== -1) this.todos.splice(idx, 1); }; handleSaveAll = (_: ojButton.ojAction) => { Runtime.EventsEmit('saveAll', JSON.stringify(this.todos())); }; } export default new RootViewModel();
- The
RootViewModel
class manages todo items with observable arrays. - Methods are defined for adding and deleting todos as well as saving all items back to storage.
The HTML body structure includes components for displaying and managing todo items effectively.
Building the Executable
To finalize your application development process, build it using:
wails build
build/bin
directory. You can run this executable file to interact with your Todo app.Users can add todo items through the interface provided by OJET. Upon clicking save, a todos.json
file will be created in their user home directory under todos_app
, storing all added items persistently.By following these steps and utilizing Wails effectively, developers can create robust cross-platform desktop applications that leverage both Go’s performance capabilities and modern web technologies for an enhanced user experience.