Support for WebAssembly will be shipped with Go 1.11 (expected in august 2018), and with it the brand new syscall/js package.

The syscall/js package allows our Go code to interact with JS references, mainly through the js.Value type with methods such as Get() , Set() or Invoke() .

I found myself wondering if there would be an easier way to manipulate JS references, and the struct tags were the first thing to come to my mind.

These are well known for their use in the encoding/json package and other packages such as ORM ones, to give hints as to how structs should be (un)marshalled:

My idea was to use them to bind struct fields “directly” to JS references, without recurring explicitly to js.Value .

So I started building a small package to implement all the mechanics to bind JS references to a struct like this:

In this article I would like to share what I have done so far, how to use it, and how I did it.

DISCLAIMERS:

I am not saying this is the right thing to do

This requires an extensive use of the reflect package hence brings some complexity

package hence brings some complexity The package I’m presenting is incomplete, full of FIXMEs and TODOs, and may be buggy, it should be used for experimenting only

This article follows my previous one about the syscall/js package API:

Prerequisites

In order to setup a working environment for building Go to WebAssembly and running it, please refer to my previous article linked above or other resources out there.

Usage

The package I made is available in the js/bind directory of my golang-wasm github repository, you may download it with the following command:

go get github.com/nlepage/golang-wasm/js/bind

It offers only two public functions:

BindGlobals is just a helper to avoid calling Bind with js.Global as a parent.

Let’s take back our previous struct example…

… and make a small main function which increments counter and alerts a message with the current value of counter :

Nothing to do on the JS side, except declaring a count variable initialized to zero in the global scope:

Now if we run the wasm binary several times without reloading the page, alert dialogs should appear with the current counter value:

The sources for a docker image with this alert counter are available in the examples/bind-counter directory of my github golang-wasm repository.

As you could see in our example, Bind() handles three different kinds of binding:

Functions

Getters

Setters

Functions

In order to bind a JS function to a struct field, you have to declare a field typed func with the same signature as the JS function you want to bind.

In our previous example, window.alert() has one string argument and has no return value, so the corresponding Go func type is func(string) .

Then you have to tag the field with the identifier of JS function you want to bind, here alert , followed by () to indicate that it is not a property you want to bind but a function.

If we would like to bind parseInt() we could use the following structure:

Once bound you may call it like a normal function:

The parenthesis following the name of the function in the tag are mandatory, without these Bind() could mistake the function for a getter or a setter.

Getters

In order to access the value of a JS reference, you need to bind a getter to this reference.

To bind a getter, you just have to declare a field typed func with no parameter and only one return value of the correct type (the return type may be one of int , float64 , bool or string ).

Then tag the field with the identifier of the property you want to access, for example to access a global variable named message you may use the following struct:

Getters also support struct return types, performing a recursive binding in this case, so you could access window.location with the following structures:

Once bound you may read window.location.href with example.Location().Href() .

Setters

In order to assign a value of a JS reference, you need to bind a setter to this reference.

To bind a setter, you just have to declare a field typed func with only one parameter of the correct type and no return value.

Then, like for getters, tag the field with the identifier of the property you want to assign.

What could be added ?

A lot more could be added to the current features supported by Bind() , including but non ehaustive:

Handling struct typed parameters in functions and setters

typed parameters in functions and setters Handling func typed parameters in functions and setters (dynamically creating callbacks)

typed parameters in functions and setters (dynamically creating callbacks) Handling composed structures

Under the hood

In order to interpret struct tags we must use the reflect package of the standard library.

The struct tags are accessible on the reflect.StructField of the different fields which can be retrieved on the reflect.Type of the struct:

Then the struct tags can be accessed via the Tag field of the reflect.StructField :

Now using the hints given by the tags we have to dynamically assign functions to the struct fields.

Again this is going to require some reflect black magic in order to “build” function values with the correct signature.

A getter (or a setter) signature is rather easy to satisfy, here is an example of how to make a reflect.Value of an int getter:

Then we can assign it to the struct field using the reflect.Value s of the struct and fields:

Building a reflect.Value matching an arbitrary signature is a little more tricky, it can be done using reflect.MakeFunc() and giving reflect.Type .

If you want to go further, have a look at the sources in the js/bind directory of my golang-wasm github repository, and why not fork it and make it a real package?

Thank you for reading, have fun !