DEV Community 👩‍💻👨‍💻

Thomas Schühly
Thomas Schühly

Posted on • Updated on

Creating a GO GUI with Alpine.js and Webview

There are a lot of options for building a GUI for Go applications.
Coming from the web development world building the frontend with HTML seems as a no-brainer.

Webview

Webview is a tiny cross-platform library for C/C++/Golang to build modern cross-platform GUIs. The goal of the project is to create a common HTML5 UI abstraction layer for the most widely used platforms.

To start using webview you need to install webview:
go get github.com/webview/webview

On windows you need to have these two dlls in the project root folder.

It supports two-way JavaScript bindings (to call JavaScript from C/C++/Go and to call C/C++/Go from JavaScript).
But writing pure javascript code for the interactivity (imo) is awful.

Alpine.js to the rescue

"Alpine.js is a rugged, minimal tool for composing behavior directly in your markup." It fits perfectly for our usecase.

You can load alpine inline or from a file. The newest version is available at unpkg.com/alpinejs

func loadAlpine() string {
    return "paste alpine.js source here"
}
Enter fullscreen mode Exit fullscreen mode

First you must initialize webview.

func main() {
    webView := webview.New(true)
    defer webView.Destroy()

    webView.SetSize(600, 600, webview.HintNone)
    webView.Init(loadAlpine())
Enter fullscreen mode Exit fullscreen mode

To execute go code with alpine we need to call webView.bind("functionName").

webView.Bind("extractSubDirectories", func(sourceFolder string) string {
  folderUrls = extractSubDirectories(sourceFolder)
    tmpl := template.Must(template.New("html").Parse(
      // language=GoTemplate
    `<div>
      {{range $vendor, $folderDetailsArray := .}}
        <div>
          <h3>Vendor: {{$vendor}}</h2>
        {{range $folderDetails := $folderDetailsArray}}
          <ul>
            <li>{{ .Path }} filecount:: {{ .FileCount }}</li>
          </ul>
        {{end}}
        </div>
      {{end}}
     </div>`))
  var html bytes.Buffer
  err := tmpl.Execute(&html, folderUrls)
  if err != nil {
    logger.WritePrint("ERROR: " + err.Error())
  }
  return html.String()
})
Enter fullscreen mode Exit fullscreen mode

To create your first page you call webView.Navigate() and supply it with your HTML. Then call webView.Run()

webView.Navigate(`data:text/html` + `<!doctype html>
<html lang="de" x-data="{ pathInput: '', table : ''}">
    <body style="padding: 2rem">
        <h1>JPEG Sorter</h1>
        <p>Input the folder where the images are stored</p>
        <input type="text" x-model="pathInput"/>

        <button @click="table = ''; table = await extractSubDirectories(pathInput);">analyse folder</button>

        <div x-html=table></div>
    </body>
</html>`)
webView.Run()
Enter fullscreen mode Exit fullscreen mode

Alpine.js

As you can see there are quite a lot of non standard html attributes, this is the magic of alpine.js.
You can create local alpine data variables in the scope of the element:

<html lang="de" x-data="{ pathInput: '', table : ''}">
Enter fullscreen mode Exit fullscreen mode

You can bind input data to the local variables with x-model

<input type="text" x-model="pathInput"/>
Enter fullscreen mode Exit fullscreen mode

But the coolest part comes now. With an @click alpine attribute we can call our go functions from the html. The extractSubDirectories() function we binded earlier in this example.

<button @click="table = await extractSubDirectories(pathInput);">
  analyse folder
</button>
Enter fullscreen mode Exit fullscreen mode

With x-html we can bind the returned html from the go function into our gui.

<div x-html=table></div>
Enter fullscreen mode Exit fullscreen mode

These are the basic steps to get webview and alpine.js working with Go.

GUI Example

GUI Example

You can look at my recent freelance project for a complete example on Github.

Top comments (4)

Collapse
 
sensorario profile image
Simone Gentili

Could you please add some GUI example (I mean, .. images) in your post?

Collapse
 
tschuehly profile image
Thomas Schühly Author

I added one :)

Collapse
 
romanesko profile image
Roman Bykovskiy

Could you explain the last line of code. Why we assigning value from variable tbl, if we declared variable table and what’s that ="executionTime">?

Collapse
 
tschuehly profile image
Thomas Schühly Author

It is just a typo, it needs to be x-html=table

In defense of the modern web

I expect I'll annoy everyone with this post: the anti-JavaScript crusaders, justly aghast at how much of the stuff we slather onto modern websites; the people arguing the web is a broken platform for interactive applications anyway and we should start over;

React users; the old guard with their artisanal JS and hand authored HTML; and Tom MacWright, someone I've admired from afar since I first became aware of his work on Mapbox many years ago. But I guess that's the price of having opinions.