form

package module
v0.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 9, 2026 License: MIT Imports: 3 Imported by: 1

README

tinywasm/form

Fast form generation from Go structs for TinyGo + WASM. Uses fmt.Fielder interface for zero-reflection data binding. Input types are assigned via fmt.Widget in the schema (generated by ormc) — no magic name matching.

Rules (Non-Negotiable)

  • No stdlib imports in library code. Never errors, strconv, strings, reflect.
  • Use github.com/tinywasm/fmt: fmt.Err("Noun", "Adjective") for errors, fmt.Convert(s).Int() instead of strconv.Atoi, fmt.Contains(s, sub) instead of strings.Contains.
  • No maps in inputs: Use slices. Maps increase WASM binary size.
  • No reflect at runtime.

Quick Start

// User struct implements fmt.Fielder (generated by ormc).
// Each field in Schema() has a Widget assigned (e.g. input.Email()).
f, err := form.New("parent-id", &User{Name: "John"})
html := f.RenderHTML()    // render to HTML string

How Field Matching Works

form.New() iterates data.Schema(). For each field:

  1. If field.Widget == nil → field is skipped (no UI binding).
  2. field.Widget.Clone(formID, fieldName) creates a positioned input instance.
  3. The result is type-asserted to input.Input — if it fails, the field is skipped.
  4. Constraint-based defaults are applied (e.g. NotNullSetRequired(true)).
  5. Current value from data.Pointers() is bound to the input.

There is no registry-based name matching. Each field's Widget is set explicitly in the schema by ormc.

Input Interface

// input.Input — defined in input/interface.go
type Input interface {
    fmt.Widget    // Type(), Validate(value string) error, Clone(parentID, name string) Widget
    dom.Component // GetID(), SetID(), RenderHTML(), Children()
}

Each concrete type (Email, Text, etc.) provides:

  • Xxx() Input — template instance for use in schema (no position).
  • Clone(parentID, name string) fmt.Widget — creates a positioned copy ready for rendering.
  • Type() string — semantic input type name (e.g. "email").
  • Validate(value string) error — validates input value via Permitted rules.

Public API

form.New(parentID string, data fmt.Fielder) (*Form, error)

Creates a Form from a Fielder. Uses data.Schema() to resolve inputs via field.Widget. Form id = parentID + "." + resolveStructName(data).

form.RegisterInput(inputs ...input.Input)

Registers custom input types globally. Used for explicit lookup via findInputByType(). Not called automatically — no init() auto-registration.

form.SetGlobalClass(classes ...string)

Sets CSS classes applied to all new forms.

Form Methods
Method Description
GetID() string Returns form's HTML id
SetSSR(bool) *Form Enables SSR mode (adds method, action, submit button)
RenderHTML() string Generates form HTML
Validate() error Validates all inputs, returns first error
SyncValues(fmt.Fielder) error Copies input values back into the provided data
ValidateData(byte, fmt.Fielder) error Server-side validation (crudp.DataValidator)
OnSubmit(func(fmt.Fielder) error) *Form Sets WASM submit callback
OnMount() WASM only — sets up event delegation
Input(fieldName string) input.Input Returns input for a field name
SetOptions(fieldName, ...fmt.KeyValue) *Form Sets options for select/radio/datalist
SetValues(fieldName, ...string) *Form Sets value programmatically
Namer Interface

Fielder types can optionally implement Namer to customize the form name:

type Namer interface {
    FormName() string
}

If not implemented, defaults to "form".

WASM Event Flow

dom.Mount("root", f)
  -> f.RenderHTML() injected into DOM
  -> f.OnMount():
      el.On("input",  fn) -> SetValues + Validate per input
      el.On("change", fn) -> same (for select/radio/checkbox)
      el.On("submit", fn) -> PreventDefault -> SyncValues(f.data) -> Validate -> onSubmit(f.data)

One listener per form (not per input) = fewer closures = smaller WASM binary.

Standard Input Types

17 built-in types in input/:

Input HTML type
Address text
Checkbox checkbox
Date date
Datalist text
Email email
Filepath text
Gender radio
Hour time
IP text
Number number
Password password
Phone tel
Radio radio
Rut text
Select select
Text text
Textarea textarea

Struct Tags

The unified input: tag is parsed by ormc at generation time to populate the fmt.Field schema:

Feature Format Example
Widget input:"type" input:"email", input:"textarea", input:"-"
Validation input:"rule" input:"required", input:"min=5", input:"max=100"
Metadata input:"key=val" input:"title=\"Label\"", input:"placeholder=\"Hint\""
Options input:"options=..." input:"options=\"1:Yes,0:No\""

Multiple rules are comma-separated: `input:"email,required,max=100"`. See docs/TAGS.md for full reference.

Runtime overrides: f.Input("Field").SetPlaceholder(), f.SetOptions("Field", ...).

Validation Engine (fmt.Permitted)

type Permitted struct {
	Letters    bool     // a-z, A-Z (and ñ/Ñ)
	Tilde      bool     // á, é, í, ó, ú
	Numbers    bool     // 0-9
	Spaces     bool     // ' '
	BreakLine  bool     // '\n'
	Tab        bool     // '\t'
	Extra      []rune   // additional allowed characters
	NotAllowed []string // disallowed substrings
	Minimum    int      // minimum length
	Maximum    int      // maximum length
}

File Map

File Responsibility
form.go Form struct, New(), Input(), SetOptions(), SetValues(), Namer
sync.go SyncValues(), pointer-based field sync
registry.go RegisterInput(), findInputByType(), SetGlobalClass()
render.go RenderHTML(), SetSSR()
validate.go Validate()
validate_struct.go ValidateData() (crudp.DataValidator)
words.go Registers form UI words into fmt dictionary
mount.go OnMount(), OnUnmount() (wasm only)
input/interface.go Input interface (embeds fmt.Widget + dom.Component)
input/base.go Base struct embedded by all inputs
input/*.go 17 concrete input implementations

Documentation Index

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GetTagOptions added in v0.0.2

func GetTagOptions(fieldTag string) ([]fmt.KeyValue, bool)

GetTagOptions extracts options from a struct field tag using fmt.TagPairs.

func ParseOptionsTag added in v0.0.2

func ParseOptionsTag(tag string) []fmt.KeyValue

ParseOptionsTag parses "key1:text1,key2:text2" format into []fmt.KeyValue.

func RegisterInput added in v0.0.2

func RegisterInput(inputs ...input.Input)

RegisterInput registers input types for field mapping.

func SetGlobalClass added in v0.0.2

func SetGlobalClass(classes ...string)

Types

type Form

type Form struct {
	Inputs []input.Input
	// contains filtered or unexported fields
}

Form represents a form instance.

func New

func New(parentID string, data fmt.Fielder) (*Form, error)

New creates a new Form from a Fielder. parentID: ID of the parent DOM element where the form will be mounted. Returns an error if any exported field has no matching registered input.

func (*Form) Children added in v0.2.1

func (f *Form) Children() []dom.Component

Children returns the form's input fields as dom components (O(1), zero-alloc).

func (*Form) GetID added in v0.0.17

func (f *Form) GetID() string

GetID returns the html id that group the form

func (*Form) Input added in v0.0.2

func (f *Form) Input(fieldName string) input.Input

Input returns the input with the given field name, or nil if not found.

func (*Form) OnSubmit added in v0.0.2

func (f *Form) OnSubmit(fn func(fmt.Fielder) error) *Form

OnSubmit sets the callback for form submission in WASM mode.

func (*Form) ParentID added in v0.0.2

func (f *Form) ParentID() string

ParentID returns the ID of the parent element.

func (*Form) RenderHTML added in v0.0.2

func (f *Form) RenderHTML() string

RenderHTML renders the form based on its SSR mode.

func (*Form) SetID added in v0.0.17

func (f *Form) SetID(id string)

SetID sets the html id that group the form

func (*Form) SetOptions added in v0.0.2

func (f *Form) SetOptions(fieldName string, opts ...fmt.KeyValue) *Form

SetOptions sets options for the input matching the given field name.

func (*Form) SetSSR added in v0.0.2

func (f *Form) SetSSR(enabled bool) *Form

SetSSR enables or disables SSR mode for this form.

func (*Form) SetValues added in v0.0.2

func (f *Form) SetValues(fieldName string, values ...string) *Form

SetValues sets values for the input matching the given field name.

func (*Form) SyncValues added in v0.0.2

func (f *Form) SyncValues(data fmt.Fielder) error

SyncValues copies all input values back into the bound struct via the Fielder's Pointers() method.

func (*Form) Validate added in v0.0.2

func (f *Form) Validate() error

Validate validates all inputs and returns the first error found.

func (*Form) ValidateData added in v0.0.26

func (f *Form) ValidateData(action byte, data fmt.Fielder) error

ValidateData validates a Fielder instance using this form's input rules. Satisfies the updated crudp.DataValidator interface (with fmt.Fielder).

type Namer added in v0.0.29

type Namer interface {
	FormName() string
}

Namer is optionally implemented by Fielder types to provide a custom name. If not implemented, the form derives the name from the first Schema field's context.

Directories

Path Synopsis
example
web command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL