Concepts of Askew’s Language

Askew defines a metalanguage based on HTML in which you define the structure of your UI components and the main site. Basically, you write normal HTML and at certain points you can insert non-HTML elements or attributes that are understood and processed by Askew. This metalanguage is also named Askew.

In Askew, a UI component is a type whose instances can be inserted at certain predefined locations in the structure of your website. They behave similarly to widgets in typical GUI libraries. UI components are defined by the user, there are no predefined components. The internal HTML used by a component is defined with Askew.

To use your components, you must define the site which contains them. A site is a single HTML document and is also written in Askew.

The build process of an Askew-based web application is as follows:

Pipeline for using askew to create a website

You process all component definitions (*.askew) and the site definition (*.asite) with the askew command line utility, which gives you generated Go source files. Then you write your application code in Go; this includes adding handlers you declared in your Askew files, for example for handling certain DOM events. Finally, you compile your code along with the generated code with GopherJS to JavaScript, and that, along with the HTML file generated by Askew, is your website. (WASM support is not yet implemented.)

The main *.asite file

Askew allows you to have multiple *.asite files in your module as long as they belong to different modules. Each *.asite generates an HTML file and for multiple *.asite files, you probably want to also have different main functions per site. We assume here that you have a single *.asite file.

An *.asite is processed as HTML document. It must begin with <!doctype html> and contain a single root element named <a:site>. That element replaces the usual <html> top element of an HTML site and will be rewritten as such when it is processed. This would be a minimal valid *.asite file:

<!doctype html>
<a:site lang="en">
  <head>
    <title>Test</title>
  </head>
  <body>
    <p>Hello, World!</p>
  </body>
</a:site>

As you can see, you can use <html>’s attributes with <a:site>. Besides those, there are some Askew-specific attributes you can set:

The HTML file will be created in the output directory specified as option of the askew command. The JavaScript path will be written as-is into a <script> tag’s src attribute, as will the path to wasm_exec.js. The path to the WASM file will be loaded via fetch. Since Askew does call neither GopherJS nor Go’s WASM compiler for you, it is your responsibility to provide the .js and .wasm files at the given path. The generated <script> element will be appended to the end of the <body> element’s content.

Packages and Imports

Askew uses Go’s concept of packages, i.e. any files in a certain directory are considered to be part of the package defined by that directory. By default, it is assumed that the package’s name equals the directory’s name. You can override this by specifying a <a:package> element, e.g.

<!doctype html>
<a:site lang="en">
  <a:package>main</a:package>
  <!-- ... -->
</a:site>

The either explicitly or implicitly defined package name of an Askew file must match with the package name in any other Askew or Go files in the same package. This means that <a:package> is required if the current package name differs from the directory’s name.

If you want to refer to components defined in other packages, you must import them with an <a:import> Element:

<!doctype html>
<a:site lang="en">
  <a:import>
    "some/package"
    alias "some/other/package"
  </a:import>
  <!-- .. -->
</a:site>

The syntax is identical to the content of the paretheses of Go’s import statement. Askew always implicitly imports the package github.com/flyx/askew/runtime, named askew, so you must not import a package with that name.

In an *.asite file, you must place <a:package> and <a:import> as direct children of the <a:site> element. You can use those elements also in *.askew files, where you must place them at top level. You can’t place them at top level in an *.asite file because the HTML parser would firmly reject that.

Embeds

As we will see in the next chapter, you will define components in *.askew files. You use those components by embedding them via the <a:embed> element. This element allows the following attributes:

An embed that is neither a list nor an optional is called a direct embed. The following code shows some examples of embeds in the main site. Note that <a:embed> is not self-closing and must be properly closed.

<!doctype html>
<a:site lang="en">
  <a:import>
    comps "example.com/components"
  </a:import>
  <head>
    <title>Example</title>
  </head>
  <body>
    <!-- A direct embed of the component named "header" which must be defined
         in the same package. The component's constructor will be called with
         the arguments (`Title`, `Subtitle`). -->
    <a:embed name="Header" type="header" args="`Title`, `Subtitle`"></a:embed>
    <nav><ul>
      <!-- A list embed.
           Can contain any number of components of the type "comps.NavItem",
           which is defined in the package comps that has been imported above.
           That type may be a component, but may also a Go interface. -->
      <a:embed name="NavItems" type="comps.NavItem" list></a:embed>
    </ul></nav>
    <main>
      <!-- The main content of the page. Has no type, which means that any
           component can be put here. This requires the embed to be optional.
           Initially, it will be empty. -->
      <a:embed name="Content" optional></a:embed>
    </main>
  </body>
</a:site>

For list and optional embeds, you can explicitly construct the initial component(s) that should be contained. You do this with the element <a:construct>, which must be put inside the <a:embed>. <a:construct> allows the following attributes:

An optional <a:embed> may contain at most one <a:construct> which may not have a a:for, a list may contain any number of <a:construct>s, a direct embed may not contain any.

The main function

Just like with regular Go code, you must write a main function as entry point. When compiling to WASM, this main file must not exit if you want event handlers to work. You can call askew.KeepAlive() to do this, it is a nop when compiling with GopherJS.