Explicit mount primitive (`@component`/`@mount`) for function templates
#115Function templates have no way to mount a Go-written component returned by a plain function. A function returning tui.Component is not covered by v0.18.1's restriction on component elements/expressions/struct calls, and generation still reaches invalid Go. We'd like an explicit mounting primitive.
Environment
- go-tui: v0.17.0 (also reproduced against latest v0.18.1)
Minimal reproduction
package repro
import tui "github.com/grindlemire/go-tui"
type row struct{}
func NewRow() tui.Component { return &row{} }
func (r *row) Render(*tui.App) *tui.Element {
return tui.New(tui.WithText("row"))
}
templ Field() {
@NewRow()
}
Actual
Generation succeeds but emits code that treats the returned tui.Component as a generated function-template view:
__tui_0.GetWatchers undefined (type tui.Component has no field or method GetWatchers)
__tui_0.Root undefined (type tui.Component has no field or method Root)
v0.18.1's release notes say function templates reject component elements, expressions, and struct calls — but a plain function returning tui.Component is not covered and still reaches invalid generated Go.
Requested
An explicit mounting primitive, e.g.
templ ModelField(w *TargetConfig) {
@component(ModelCatalogLoading(w))
}
(or equivalently @mount(...)) that emits an app.Mount call with a stable key mechanism and type-checks that the expression implements tui.Component. We'd prefer an explicit primitive over implicit name/return-type inference.
Thanks for the report! I'll look into this.
Thanks for the clear repro. I put up #117 which makes this error explicit rather than emit broken Go code.
I don't think we will be able to support adding @component(...)/@mount(...), for two reasons:
- Struct based components can't exist in a pure function template. Mounting needs a
*tui.Appand a stable owner for the component cache. A function templ compiles to a plain builder that constructs its tree eagerly, before any app exists. A keyword can't fix that, it would take redesigning function templs to build lazily insideRender(app). - I support this already using a method based render template.
@Factory()compiles toapp.Mountwith a stable key, and the closure returnstui.Component, so the compiler type-checks the expression. The keyword would add syntax without adding capability.
So the supported way to mount a Go-written component is in a struct component rather than a pure one.
I have more info about the difference between the component types in the docs here: https://go-tui.dev/guide/components
If that doesn't cover your use case I would love to know more about what you are trying to do.
Thanks for looking into it 👍 Long-term I think it's worth revisiting "the supported way to mount a Go-written component is in a struct component rather than a pure one". For example, this is a friction to common case of having imperative primitives (go-written) with declarative higher-order components (gsx). Think, encapsulating interaction grammar or inputs into components and building mostly gsx ui around them