Files
yellowjacket/pkg/templcomp/struct2html.templ
T

80 lines
2.0 KiB
Templ

package templcomp
import "reflect"
import "log/slog"
// Formable allows structs to implement custom form element generation code
type Formable interface {
ToFormElement() templ.Component
}
// GetForm tries to take any type and return form HTML
templ ToForm(i any, postURL templ.SafeURL, name string) {
<form hx-post={ postURL }>
@ToFormField(i, name)
<input type="submit"/>
</form>
}
// GetFormElement tries to take any type and return form field HTML
templ ToFormField(x any, name string) {
{{
xValue := reflect.ValueOf(x)
xType := reflect.TypeOf(x)
xIsFormable := xType.Implements(reflect.TypeOf((*Formable)(nil)).Elem())
}}
if xIsFormable {
{{ toFormElementMethod := xValue.MethodByName("ToFormElement") }}
if toFormElementMethod.IsValid() {
@toFormElementMethod.Call([]reflect.Value{})[0].Interface().(templ.Component)
{{ return }}
} else {
{{ slog.Debug("invalid ToFormElement method on Formable", "xType", xType.Name()) }}
}
}
switch xType.Kind() {
case reflect.Func:
{{ return }}
case reflect.Pointer:
if xValue.IsNil() {
@ToFormField(reflect.New(xType.Elem()), name)
} else {
@ToFormField(xValue.Elem().Interface(), name)
}
case reflect.Struct:
<section>
<h3>{ name }</h3>
for i := 0; i < xType.NumField(); i++ {
{{
fieldType := xType.Field(i)
fieldValue := xValue.Field(i)
if !fieldType.IsExported() {
continue
}
fieldName := fieldType.Name
if tag := fieldType.Tag.Get("form"); len(tag) != 0 {
fieldName = tag
}
}}
if fieldType.Type.Kind() == reflect.Pointer {
@ToFormField(fieldValue.Interface(), fieldName)
{{ continue }}
} else if fieldType.Type.Kind() == reflect.Func {
{{ continue }}
}
<p>
<label for={ fieldName }>
{ fieldName }
</label>
@ToFormField(fieldValue.Interface(), fieldName)
</p>
}
</section>
case reflect.String:
<input name={ name } type="text"/>
default:
<p>unsupported: { xType.Name() }</p>
<!-- TODO: MORE TYPES -->
}
}