Defaults it knows about
The generator read name: 'anon' out of define(), so a Name left empty
or set to "anon" is left off the element entirely. Smaller HTML, and
only a layer that knows the component’s defaults can do it.
JavaScript owns what happens inside the component. Go owns what goes into it
and what the page is made of. setup() runs in a browser, so it stays in
JavaScript; everything around it can be typed.
Write the component.
import { define, html, css, computed } from 'alacris';
/** * A person, at a glance. * * @prop {string[]} tags the labels shown under the name * @fires greet {name: string} - the user said hello * @slot title - replaces the heading */define('user-card', { props: { name: 'anon', age: 0, tags: [] }, styles: css`:host { display: block } h3 { margin: 0 }`, setup({ name, age, tags }, host) { const grown = computed(() => age() >= 18); return html` <h3><slot name="title">${name}</slot></h3> <p>${() => (grown() ? 'adult' : 'minor')} · ${() => tags().join(', ')}</p> <button @click=${() => host.emit('greet', { name: name() })}>say hi</button>`; },});The JSDoc block is not decoration. define() says what a prop is called and
how it is coerced. The tags say the rest: that tags is a list of strings
rather than a list of anything, that a greet event exists and what it
carries, that there is a title slot.
Generate the wrapper.
go run github.com/bmartel/alacris-go/cmd/alacris-go generate ./web -o ./internal/componentsalacris-go: 1 component(s) -> ./internal/components wrote internal/components/components_gen.go wrote internal/components/alacris_gen.goRead what you got.
const UserCardTag = "user-card"
const UserCardSlotTitle = "title"const UserCardEventGreet = "greet"
type UserCardGreetDetail struct { Name string `json:"name"`}
type UserCardProps struct { // Name ... // The component defaults it to 'anon'. Name string Age int // Tags is the labels shown under the name. // // Crosses as JSON in the tags attribute. Tags []string}
func UserCard(p UserCardProps) *alacris.ElementUse it.
@components.UserCard(components.UserCardProps{ Name: "Ada", Age: 36, Tags: []string{"math", "code"},}) { <span slot={ components.UserCardSlotTitle }>Ada Lovelace</span>}return alacris.E("user-card"). Prop("name", "Ada"). Prop("age", 36). Prop("tags", []string{"math", "code"})<user-card name="Ada" age="36" tags="["math","code"]"></user-card>Props are kebab-cased and encoded by their Go type.
Defaults it knows about
The generator read name: 'anon' out of define(), so a Name left empty
or set to "anon" is left off the element entirely. Smaller HTML, and
only a layer that knows the component’s defaults can do it.
Three more things it did:
maxCount becomes max-count
using the same transformation define.js uses, quirks and all. A hand-written
attribute name that gets this wrong produces an element that silently ignores
it.UserCardGreetDetail is what the
live layer decodes into, so a rename in
the component’s @fires tag becomes a Go compile error rather than a handler
that receives an empty struct.*bool where it matters. A boolean prop whose default is true cannot
be a plain bool. Go’s zero value is false, so the field could never mean
“leave it alone”. See Props and encoding.//go:generate go run github.com/bmartel/alacris-go/cmd/alacris-go generate ./web -o ./internal/components//go:generate go run github.com/a-h/templ/cmd/templ@latest generateOrder matters: the wrappers first, because the templates use them.
In CI, check rather than regenerate:
alacris-go check ./web -o ./internal/components