Skip to content

Components

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

Table of Contents

Description

Components are structs that let you split the UI into independent and reusable pieces.

Here is a component example that shows a hello world:

type hello struct {
    app.Compo
    name string
}

func (h *hello) Render() app.UI {
    return app.Div().Body(
        app.H1().Body(
            app.Text("Hello "),
            app.Text(h.name),
        ),
        app.Input().
            Value(h.name).
            OnChange(h.OnInputChange),
    )
}

func (h *hello) OnInputChange(ctx app.Context, e app.Event) {
    h.name = ctx.JSSrc().Get("value").String()
    h.Update()
}

Create

Creating a component is done by embedding Compo into a struct:

type hello struct {
    app.Compo   // <-- The base component implementation
    name string
}

Customize

Once created, the next thing to do is define the component appearance. This is done by implementing the Render() method:

func (h *hello) Render() app.UI {
    return app.Div().Body(
        app.H1().Body(
            app.Text("Hello "),
            app.Text(h.name),
        ),
        app.Input().
            Value(h.name).
            OnChange(h.OnInputChange),
    )
}

It returns an UI element that can be either an HTML element or another component.

See the Declarative Syntax topic for more info about how to shape a component.

Update

The Render() method defines the component appearance.

In the hello world example above, the rendering uses the name component field to define the title and the input default value.

It also set up an event handler that is called when the input change:

func (h *hello) OnInputChange(ctx app.Context, e app.Event) {
    h.name = ctx.JSSrc().Get("value").String()
    h.Update()
}

At each change, the input value is assigned to the component name field.

Changing the value of a variable used in the Render() method does not update the UI.

The way to tell the browser that the component appearance has to be updated is by calling the Compo.Update() method:

h.Update()

Under the hood, the update method creates a new state that is compared to the current one. Then it performs a diff and updates only the HTML nodes where differences are found.

Composing Components

Components can refer to other components in their Render() method.

Here is an example that shows "Foo, Bar!" by using a component that embeds another one:

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

func (f *foo) Render() app.UI {
    return app.P().Body(
        app.Text("Foo, "),
        &bar{},            // <-- bar component
    )
}

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

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

Lifecycle

Components often use other resources to represent a UI. Those resources might require to be initialized or released.

The go-app package provides interfaces that allow calling functions at different times during the component lifecycle.

lifecycle

Implementing them in a component is a good place for initializing or free resources.

OnMount

A component is mounted when it is inserted into the webpage DOM.

When the Mounter interface is implemented, the OnMount() method is called right after the component is mounted.

type foo struct {
    app.Compo
}

func (f *foo) OnMount(ctx app.Context) {
    fmt.Println("component mounted")
}

OnNav

A component is navigated when a page where it is the body root is loaded, reloaded or navigated from an anchor link or an HREF change.

When the Navigator interface is implemented, the OnNav() method is called each time the component is navigated.

type foo struct {
    app.Compo
}

func (f *foo) OnNav(ctx app.Context, u *url.URL) {
    fmt.Println("component navigated:", u)
}

OnDismount

A component is dismounted when it is removed from the webpage DOM.

When the Dismounter interface is implemented, the OnDismount() method is called right after the component is dismounted.

type foo struct {
    app.Compo
}

func (f *foo) OnDismount() {
    fmt.Println("component dismounted")
}