diff --git a/website/docs/howdoesitwork.mdx b/website/docs/howdoesitwork.mdx index 015f98c96..f8ee0d719 100644 --- a/website/docs/howdoesitwork.mdx +++ b/website/docs/howdoesitwork.mdx @@ -123,9 +123,10 @@ option [OnBeforeClose](reference/options.mdx#onbeforeclose). #### Method Binding The `Bind` option is one of the most important options in a Wails application. It specifies which struct methods -to expose to the frontend. When the application starts, it examines the struct instances listed in the `Bind` field in -the options, determines which methods are public (starts with an uppercase letter) and will generate Javascript versions -of those methods that can be called by the frontend code. +to expose to the frontend. Think of structs like "controllers" in a traditional web application. When the application +starts, it examines the struct instances listed in the `Bind` field in the options, determines which methods are +public (starts with an uppercase letter) and will generate Javascript versions of those methods that can be called +by the frontend code. :::info Note @@ -158,8 +159,6 @@ func main() { Width: 1024, Height: 768, Assets: &assets, - OnStartup: app.startup, - OnShutdown: app.shutdown, Bind: []interface{}{ app, }, @@ -174,67 +173,41 @@ type App struct { ctx context.Context } -func (b *App) startup(ctx context.Context) { - b.ctx = ctx -} - -func (b *App) shutdown(ctx context.Context) {} - -func (b *App) Greet(name string) string { +func (a *App) Greet(name string) string { return fmt.Sprintf("Hello %s!", name) } ``` You may bind as many structs as you like. Just make sure you create an instance of it and pass it in `Bind`: -```go {10-12} -... +```go {8-10} + //... err := wails.Run(&options.App{ Title: "Basic Demo", Width: 1024, Height: 768, Assets: &assets, - OnStartup: app.startup, - OnShutdown: app.shutdown, Bind: []interface{}{ app, &mystruct1{}, &mystruct2{}, }, }) -... + ``` -The bound methods are located in the frontend at `window.go...`. -In the example above, we bind `app`, which has one public method `Greet`. -This can be called in Javascript by calling `window.go.main.App.Greet`. -These methods return a Promise. A successful call will result in the first return value from the Go call to be passed -to the `resolve` handler. An unsuccessful call is when a Go method that has an error type as it's second return value, -passes an error instance back to the caller. This is passed back via the `reject` handler. -In the example above, `Greet` only returns a `string` so the Javascript call will never reject - unless invalid data -is passed to it. +When you run `wails dev` (or `wails generate module`), a frontend module will be generated containing the following: + - Javascript bindings for all bound methods + - Typescript declarations for all bound methods + - Typescript definitions for all Go structs used as inputs or outputs by the bound methods -All data types are correctly translated between Go and Javascript. Even structs. If you return a struct from a Go call, -it will be returned to your frontend as a Javascript map. Note: If you wish to use structs, you **must** define `json` struct -tags for your fields! - -:::info Note -Anonymous nested structs are not supported at this time. -::: - -It is also possible to send structs back to Go. Any Javascript map passed as an argument that -is expecting a struct, will be converted to that struct type. To make this process a lot easier, in `dev` mode, -a TypeScript module is generated, defining all the struct types used in bound methods. Using this module, it's possible -to construct and send native Javascript objects to the Go code. - -More information on Binding can be found in the [Binding Methods](guides/application-development.mdx#binding-methods) -section of the [Application Development Guide](guides/application-development.mdx). +This makes it incredibly simple to call Go code from the frontend, using the same strongly typed datastructures. ## The Frontend ### Overview -The frontend is a collection of files rendered by webkit. It's like a browser and webserver in one. +The frontend is a collection of files rendered by webkit. It''s like a browser and webserver in one. There is virtually[^1] no limit to which frameworks or libraries you can use. The main points of interaction between the frontend and your Go code are: @@ -247,48 +220,61 @@ the frontend and your Go code are: ### Calling bound Go methods -All bound Go methods are available at `window.go...`. As stated in -the previous section, these return a Promise where a successful call returns a value to the -resolve handler and an error returns a value to the reject handler. +When you run your application with `wails dev`, it will automatically generate Javascript bindings for your structs in a +directory called `wailsjs/go` (You can also do this by running `wails generate module`). The generated files mirror the +package names in your application. In the example above, we bind `app`, which has one public method `Greet`. This will +lead to the generation of the following files: -```go title="mycode.js" -window.go.main.App.Greet("Bill").then((result) => { - console.log("The greeting is: " + result); -}) +```bash +wailsjs + └─go + └─main + ├─App.d.ts + └─App.js +``` +Here we can see that there is a `main` package that contains the Javascript bindings for the bound `App` struct, as well +as the Typescript declaration file for those methods. To call `Greet` from our frontend, we simply import the method and +call it like a regular Javascript function: + +```javascript + // ... + import {Greet} from '../wailsjs/go/main/App' + + function doGreeting(name) { + Greet(name).then((result) => { + // Do something with result + }) + } +``` +The Typescript declaration file gives you the correct types for the bound methods: + +```ts +export function Greet(arg1:string):Promise; ``` -When running the application in `dev` mode, a javascript module is generated that wraps these -methods with JSDoc annotations. This really help with development, especially as most -IDEs will process JSDoc to provide code completion and type hinting. This module is called `go` -and is generated in the directory specified by the `wailsjsdir` flag. In this module is a file -called `bindings.js` containing these wrappers. For the above example, the file contains the -following code: +The generated methods return a Promise. A successful call will result in the first return value from the Go call to be passed +to the `resolve` handler. An unsuccessful call is when a Go method that has an error type as it''s second return value, +passes an error instance back to the caller. This is passed back via the `reject` handler. +In the example above, `Greet` only returns a `string` so the Javascript call will never reject - unless invalid data +is passed to it. -```js title="bindings.js" -const go = { - main: { - App: { - /** - * Greet - * @param {Person} arg1 - Go Type: string - * @returns {Promise} - Go Type: string - */ - Greet: (arg1) => { - return window.go.main.App.Greet(arg1); - }, - }, - }, -}; -export default go; -``` +All data types are correctly translated between Go and Javascript. Even structs. If you return a struct from a Go call, +it will be returned to your frontend as a Javascript class. Note: If you wish to use structs, you **must** define +`json` struct tags for your fields! -#### Support for structs +:::info Note +Anonymous nested structs are not supported at this time. +::: -There is also additional support for Go methods that use structs in their signature. All Go structs -specified by bound method (either as parameters or return types) will have Typescript versions auto -generated as part of the Go code wrapper module. Using these, it's possible to share the same data -model between Go and Javascript. These models align with the JSDoc annotations, empowering IDE code -completion. +It is possible to send structs back to Go. Any Javascript map/class passed as an argument that +is expecting a struct, will be converted to that struct type. To make this process a lot easier, in `dev` mode, +a TypeScript module is generated, defining all the struct types used in bound methods. Using this module, it''s possible +to construct and send native Javascript objects to the Go code. + +There is also support for Go methods that use structs in their signature. All Go structs +specified by a bound method (either as parameters or return types) will have Typescript versions auto +generated as part of the Go code wrapper module. Using these, it''s possible to share the same data +model between Go and Javascript. Example: We update our `Greet` method to accept a `Person` instead of a string: @@ -309,76 +295,77 @@ func (a *App) Greet(p Person) string { } ``` -Our `bindings.js` file has now been updated to reflect the change: +The `wailsjs/go/main/App.js` file will still have the following code: -```js title="bindings.js" -const go = { - main: { - App: { - /** - * Greet - * @param {Person} arg1 - Go Type: main.Person - * @returns {Promise} - Go Type: string - */ - Greet: (arg1) => { - return window.go.main.App.Greet(arg1); - }, - }, - }, -}; -export default go; +```js title="App.js" +export function Greet(arg1) { + return window['go']['main']['App']['Greet'](arg1); +} ``` -Alongside `bindings.js`, there is a file called `models.ts`. This contains our Go structs in TypeScript form: +But the `wailsjs/go/main/App.d.ts` file will be updated with the following code: + +```ts title="App.d.ts" +import {main} from '../models'; + +export function Greet(arg1:main.Person):Promise; +``` + +As we can see, the "main" namespace is imported from a new "models.ts" file. This file contains all the struct definitions +used by our bound methods. In this example, this is a `Person` struct. If we look at `models.ts`, we can see how the models +are defined: ```ts title="models.ts" -export class Address { - street: string; - postcode: string; +export namespace main { - static createFrom(source: any = {}) { - return new Address(source); - } + export class Address { + street: string; + postcode: string; - constructor(source: any = {}) { - if ("string" === typeof source) source = JSON.parse(source); - this.street = source["street"]; - this.postcode = source["postcode"]; - } -} -export class Person { - name: string; - age: number; - address?: Address; + static createFrom(source: any = {}) { + return new Address(source); + } - static createFrom(source: any = {}) { - return new Person(source); - } + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.street = source["street"]; + this.postcode = source["postcode"]; + } + } + export class Person { + name: string; + age: number; + address?: Address; - constructor(source: any = {}) { - if ("string" === typeof source) source = JSON.parse(source); - this.name = source["name"]; - this.age = source["age"]; - this.address = this.convertValues(source["address"], Address); - } + static createFrom(source: any = {}) { + return new Person(source); + } - convertValues(a: any, classs: any, asMap: boolean = false): any { - if (!a) { - return a; - } - if (a.slice) { - return (a as any[]).map((elem) => this.convertValues(elem, classs)); - } else if ("object" === typeof a) { - if (asMap) { - for (const key of Object.keys(a)) { - a[key] = new classs(a[key]); - } - return a; - } - return new classs(a); - } - return a; - } + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.name = source["name"]; + this.age = source["age"]; + this.address = this.convertValues(source["address"], Address); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } } ``` @@ -386,22 +373,23 @@ So long as you have TypeScript as part of your frontend build configuration, you the following way: ```js title="mycode.js" -import go from "./wailsjs/go/bindings"; -import { Person } from "./wailsjs/go/models"; + import {Greet} from '../wailsjs/go/main/App' + import {main} from '../wailsjs/go/models' -let name = ""; - -function greet(name) { - let p = new Person(); - p.name = name; - p.age = 42; - go.main.App.Greet(p).then((result) => { - console.log(result); - }); -} + function generate() { + let person = new main.Person() + person.name = "Peter" + person.age = 27 + Greet(person).then((result) => { + console.log(result) + }) + } ``` -The combination of JSDoc and TypeScript generated models makes for a powerful development environment. +The combination of generated bindings and TypeScript models makes for a powerful development environment. + +More information on Binding can be found in the [Binding Methods](guides/application-development.mdx#binding-methods) +section of the [Application Development Guide](guides/application-development.mdx). ### Calling runtime methods