Askew

A minimal framework for writing dynamic websites in pure Go


Askew is a framework that assists you in using Go for the client-side code of your website. It is primarily a code generator which reads in annotated HTML code and snippets, and outputs a Go API providing access to the website structure described in that annotated HTML.

For using Go in the browser, you need to compile your code with either GopherJS or Go’s native WASM target.

Askew’s philosophy is to hide the DOM from the code by default and require annotations to expose certain parts of it, so that it can be read and/or manipulated from inside your code. The goal is to improve maintainability of your code by explicitly stating which parts of your HTML can be modified at runtime. A fallback is provided to access raw DOM nodes to do things that aren’t directly supported by Askew.

Installation

go get github.com/flyx/askew

You need at least Go 1.12 since Askew uses Go modules.

Usage

Askew will locate the Go module you are currently in, search its content for Askew source files, and process them. Source files are identified by their extensions .askew and .asite, the latter acting as the entry point that defines the basic HTML structure of the site. The documentation covers writing such files.

For each <name>.askew file, A file <name>.askew.go will be generated; a <name>.asite file will generate <name>.asite.go and also index.html. You can then write your Go code that interacts with the generated code and compile it to a JavaScript file (if you’re using GopherJS) that will be loaded by index.html.

For more information about usage, see the generator’s documentation.

Example

This example assumes all files reside in a directory named main.

The following code defines a Component Greeter in a file greeter.askew.

<a:component name="Greeter">
	<a:handlers>
		greet()
	</a:handlers>
	<form a:capture="submit:greet() {preventDefault}">
		<label for="name">Name:</label>
		<input type="text" name="name" required
				a:bindings="prop(value):Name">
		<button type="submit">Greet me!</button>
	</form>
</a:component>

It specifies that the generated type Greeter will have a function greet that does not take any arguments. This function will not be generated by Askew.

With a:capture, the event submit is captured and will call greet. Furthermore, the default action of the event (submitting the form) is prevented.

To be able to access the content of the text input, it specifies with a:bindings that the DOM property value shall be available under the name Name.

Now we implement greet in a Go file handlers.go in the same directory:

package main

import "syscall/js"

func (g *Greeter) greet() {
	js.Global().Call("alert", "Hello, "+g.Name.Get()+"!")
}

As we can see, the Name field of the Greeter has a function Get to retrieve its value. The returned value is a string which is the default type for DOM property values (this can be customized for each bound property). We use the syscall/js interface to call the JavaScript function alert.

Now we write main.asite to use our Greeter component somewhere:

<!doctype html>
<a:site lang="en">
	<head>
		<title>Greeter Test</title>
	</head>
	<body>
		<a:embed type="Greeter" name="myGreeter"></a:embed>
	</body>
</a:site>

<a:site> replaces the root <html> tag to make it clear that this HTML needs to be processed by Askew. <a:embed> creates an instance of our Greeter component. This instance will be available via the global variable myGreeter.

Finally, we need a main function in main.go:

package main

//go:generate askew

func main() {
	myGreeter.Name.Set("World")
}

Here, we set the default value of our text input to be World. Also we instruct go generate to execute Askew on our module.

Now, let’s build it:

go generate
gopherjs build

This will generate index.html and main.js. Open index.html in your browser to test your site!

Project Status

This is a child project of QuestScreen and does not have a standalone roadmap. Features are added as needed.

Currently, the best use of this project is probably to serve as inspiration. Many things done here could be done better with decent effort. For example, basing the syntax on HTML was done because it was easy and delivered quick results. There are surely nicer ways to annotate your component’s HTML.

License

This project is licensed under the terms of the MIT license.