component.mdin: /docs/internal/components/


We build a standard component to demo some things that you can do with things the library.

import (
	"context"
	_ "embed"
	"fmt"

	"github.com/stanistan/veun"
	t "github.com/stanistan/veun/template"
)

I don't know exactly why I feel like prefacing the name of the interface with I here, but given the overloading of component, I'm going to do it.

Each component is first and foremost a veun.AsView, and we attach behavior to it. For us the behavior is silly, we get metadata, a description.

type IComponent interface {
	veun.AsView
	Description() string
}

We also have a concrete view that we're creating from the interface.

func View(c IComponent) component {
	return component{
		Type:        fmt.Sprintf("%T", c),
		Description: c.Description(),
		Body:        c,
	}
}

Our component uses the component.tpl in this directory.

//go:embed component.tpl
var tpl string
var componentTpl = t.MustParse("component", tpl)

type component struct {
	Type, Description string

	Body      veun.AsView
    BodyClass string
}

And the view that we create has an error handler based on the component itself.

func (v component) View(ctx context.Context) (*veun.View, error) {
	return veun.V(v.template()).WithErrorHandler(errorHandler{v}), nil
}

The template is reusable so that we can embed it into the errorView.

func (v component) template() t.Template {
	return t.Template{
		Tpl:   componentTpl,
		Slots: t.Slots{"body": v.Body},
		Data:  v,
	}
}

many components

We can create our own components container, similar to veun.Views.

type Views []IComponent

func (v Views) View(ctx context.Context) (*veun.View, error) {
    items := make(veun.Views, len(v))
    for idx, c := range v {
        items[idx] = View(c)
    }

    return items.View(ctx)
}