Skip to content

Declarative Syntax

maxence-charriere edited this page Jun 28, 2020 · 17 revisions

Table of Contents

Description

Writing components requires to describe how the UI looks like.

go-app provides interfaces and functions that are based on HTML elements. By using a chaining mechanism and the Go syntax, writing a UI is done in a declarative fashion, without using another language.

Here is an example that describes a title and its text:

    ui := app.Div().Body(
        app.H1().
            Class("title").
            Body(
                app.Text("Build a GUI with Go"),
            ),
        app.P().
            Class("text").
            Body(
                app.Text("Just because Go and this package are really awesome!"),
            ),
    )

HTML elements

The package provides an interface for each standard HTML element.

Here is a simplified version of the interface for a <div>:

type HTMLDiv interface {
    // Attributes:
    Body(nodes ...Node) HTMLDiv
    Class(v string) HTMLDiv
    ID(v string) HTMLDiv
    Style(k, v string) HTMLDiv

    // Event handlers:
    OnClick(h EventHandler) HTMLDiv
    OnKeyPress(h EventHandler) HTMLDiv
    OnMouseOver(h EventHandler) HTMLDiv
}

Create

Creating an HTML element is done by calling a function named after its name:

div := app.Div()

Attributes

Element interfaces provide methods to set their attributes:

div := app.Div().Class("my-class")

Multiple attributes are set by using the chaining mechanism:

div := app.Div().
    ID("id-name").
    Class("class-name")

Style

Style is an attribute that sets the element style with CSS. Use the Style() method with a CSS property name and value to style the HTML element.

div := app.Div().Style("width", "400px")

Multiple styles can be set by calling the Style() method successively:

div := app.Div().
    Style("width", "400px").
    Style("height", "200px").
    Style("background-color", "deepskyblue")

Event handlers

Event handlers are functions that are called when an HTML event occurs. They must have the following signature:

func(ctx app.Context, e app.Event)

Like attributes, element interfaces provide methods to bind event handlers to given events:

onClick := func(ctx app.Context, e app.Event) {
    fmt.Println("onClick is called")
}

div := app.Div().OnClick(onClick)

Context is a context that can be used with any function accepting a context.Context. It is cancelled when the source of the event is dismounted. Source element value can be retrieved by the JSSrc method:

onChange := func(ctx app.Context, e app.Event) {
    v := ctx.JSSrc().Get("value")
}

ctx.JSSrc() and Event are JavaScript objects wrapped in Go interfaces. See the JS Values topic to know more about it.

Standard elements

Standard elements are elements that can contain other elements. To do so they provide the Body() method which takes other elements as parameters:

div := app.Div().Body(
    app.H1().Body(
        app.Text("Title"),
    ),
    app.P().Body(
        app.Text("Content"),
    ),
)

Self closing elements

Self-closing elements are elements that cannot contain other elements.

img := app.Img().Src("/myImage.png")
br := app.Br()

Text

Text represents simple HTML text. They are created by calling the Text() function:

text := app.Text("hello world")

Raw elements

There is some case where some raw HTML code might be required. It can be done by using the Raw() function with HTML code as argument:

svg := app.Raw(`
<svg width="100" height="100">
    <circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
</svg>
`)

Be aware that using this function is unsafe since there is no check on HTML construct or format.

Components

Components are structs that let you split the UI into independent and reusable pieces. They can be embedded within a standard HTML element:

// bar component
type bar struct {
    app.Compo
}

func (b *bar) Render() app.UI {
    return app.Text("bar")
}

// A function that returns a div that contains a text and a bar component.
func fooBar() app.UI {
    return app.Div().Body(
        app.Text("foo"),
        &bar{},
    )
}

Condition

A Condition is a construct that selects the UI elements that satisfy a condition. They are created by calling the If() function.

If

Here is an If example that shows a title only when the showTitle value is true:

var showTitle bool

div := app.Div().Body(
    app.If(showTitle,
        app.H1().Body(        // Shown when showTitle == true
            app.Text("hello"),
        ),
    ),
)

ElseIf

Here is an ElseIf example that shows a title in different colors depending on an int value:

var value int

div := app.Div().Body(
    app.If(value > 7,
        app.H1().
            Style("color", "green").
            Body(
                app.Text("Good!"),
            ),
    ).ElseIf(value < 4,
        app.H1().
            Style("color", "red").
            Body(
                app.Text("Bad!"),
            ),
    ).Else(
        app.H1().
            Style("color", "orange").
            Body(
                app.Text("So so!"),
            ),
    ),
)

Else

Here is an Else example that shows a simple text when the showTitle value is false:

var showTitle bool

div := app.Div().Body(
    app.If(showTitle,
        app.H1().Body(
            app.Text("hello"),
        ),
    ).Else(
        app.Text("world"),     // Shown when showTitle == false
    ),
)

Range

Range represents a range loop that shows UI elements generated from a slice or map. They are created by calling the Range() function.

Slice

Here is a slice example that shows an unordered list from a []string:

data := []string{
    "hello",
    "go-app",
    "is",
    "sexy",
}

ul := app.Ul().Body(
    app.Range(data).Slice(func(i int) app.UI {
        return app.Li().Body(
            app.Text(data[i]),
        )
    }),
)

Map

Here is a map example that shows an unordered list from a map[string]int:

data := map[string]int{
    "Go":         10,
    "JavaScript": 4,
    "Python":     6,
    "C":          8,
}

ul := app.Ul().Body(
    app.Range(data).Map(func(k string) app.UI {
        s := fmt.Sprintf("%s: %v/10", k, data[k])

        return app.Li().Body(
            app.Text(s),
        )
    }),
)

Context menu

go-app package provides a built-in context menu.

context menu

It is created by calling the NewContextMenu() function with a list of MenuItem. Here is an example that displays a context menu with 2 items and a separator:

app.NewContextMenu(
    app.MenuItem().
        Label("item 1").
        OnClick(func(src app.Value, e app.Event) {
            fmt.Println("item 1 clicked")
        }),
    app.MenuItem().Separator(),
    app.MenuItem().
        Label("item 2").
        OnClick(func(src app.Value, e app.Event) {
            fmt.Println("item 2 clicked")
        }),
)