ui

package
v0.702.0 Latest Latest
Warning

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

Go to latest
Published: Aug 21, 2026 License: MIT Imports: 25 Imported by: 3

README

github.com/linkdata/jaws/lib/ui

Package ui contains the standard JaWS widgets, template helpers, and the RequestWriter used from Go templates.

Documentation

Overview

Package ui contains the standard JaWS widgets and template helpers.

Its main building blocks are HTMLInner for dynamic inner HTML; Input, InputText, InputBool, and InputDate for typed controls; Number and Range for numeric controls; Container, Tbody, and Select for dynamic children; and Template, Handler, and RequestWriter for template integration.

Every non-nil value used as a github.com/linkdata/jaws.UI must be comparable at runtime and equal to itself, and is scoped to one Request. Construct fresh widgets for each Request; they may share synchronized application state, binders, handlers, and tags.

Within one Request, a widget normally backs one live github.com/linkdata/jaws.Element. Widgets based on HTMLInner, plus Img, Option, Template, Container, Tbody, and Select, support multiple live Elements under their concrete contracts. Input widgets and JsVar require distinct widget values.

NewContainer, NewTbody, NewSelect, and NewTemplate return definition values. Use them as values; taking their addresses replaces definition equality with pointer identity and is unsupported.

HTML-inner widgets route content through github.com/linkdata/jaws/lib/bind.MakeHTMLGetter. Existing github.com/linkdata/jaws/lib/bind.HTMLGetter values are used unchanged, and plain strings and html/template.HTML are trusted HTML. Adapters for string-valued github.com/linkdata/jaws/lib/bind.Getter and github.com/linkdata/jaws/lib/bind.Binder values and fmt.Stringer output are escaped. Raw html/template.HTMLAttr parameters are also trusted. Escape untrusted text before it reaches a trusted form.

Browser input, click, and context-menu events are forwarded only while the WebSocket is open and are not replayed. Native form reset does not update Go bindings, and independently bound Radio values do not become one server-side group by sharing an HTML name; see RequestWriter.RadioGroup.

Each browser-to-server WebSocket message is limited to 32 KiB by github.com/linkdata/jaws.Request.ServeHTTP. Standard widgets do not chunk payloads. An oversized message closes the connection, and its read-limit error is retained in the Request cancellation cause, which is passed to github.com/linkdata/jaws.Jaws.Log.

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrElementStateUnclaimed errElementStateUnclaimed

ErrElementStateUnclaimed reports an update of an Element that no Template rendered.

Template.JawsUpdate reports this error through jaws.Request.MustLog instead of returning it.

View Source
var ErrIllegalJsVarName errIllegalJsVarName

ErrIllegalJsVarName is returned when a JsVar name is missing, is not a string, has invalid syntax, or is reserved.

Valid names begin with an ASCII letter, underscore, or dollar sign, and contain only ASCII letters, digits, underscores, and dollar signs. The exact name "__proto__" is reserved.

View Source
var ErrIllegalJsVarPath = errors.New("jsvar: path contains illegal protocol byte (tab, newline, carriage return or equals)")

ErrIllegalJsVarPath reports that a JsVar path contains a protocol byte.

JsVar.JawsSetPath returns it for a path containing a tab, newline, carriage return, or equals sign, without applying or broadcasting the change. JsVar.JawsInput applies the same check to incoming browser writes.

View Source
var ErrJsVarArgumentType = errors.New("expected jaws.UI or JsVarMaker")

ErrJsVarArgumentType is returned when RequestWriter.JsVar receives an argument that is neither a JaWS UI nor a JsVarMaker.

View Source
var ErrJsVarTooLarge = errors.New("jsvar: JSON size check failed")

ErrJsVarTooLarge reports a failed client-writable JsVar size check.

JSONSizeCheck returns an error matching ErrJsVarTooLarge when the tentative value exceeds its configured maximum or cannot be marshaled. A matching error from JsVar.ClientCheck makes JsVar.JawsInput reject the write and return ErrJsVarTooLarge. It also aborts the associated jaws.Request, when present; the request cancellation cause retains the detailed check error.

View Source
var ErrMissingTemplate errMissingTemplate

ErrMissingTemplate is returned when trying to render an undefined template by name.

Functions

func Clickable deprecated added in v0.304.0

func Clickable(innerHTML any, onClick func(elem *jaws.Element, click jaws.Click) (err error)) jaws.ClickHandler

Clickable returns an object implementing bind.HTMLGetter, jaws.ClickHandler and tag.TagGetter.

innerHTML is passed to bind.MakeHTMLGetter, which may or may not provide tags.

Deprecated: use New(innerHTML).Clicked(...) directly.

func Handler

func Handler(jw *jaws.Jaws, name string, dot any) http.Handler

Handler returns an http.Handler that renders the named template.

The returned handler can be registered directly with a router. Each request results in the template being looked up through the configured template lookupers and rendered with a With value as the template data, exposing dot through its Dot field. Unless the response already has a Content-Type, Handler sets it to "text/html; charset=utf-8" when rendering writes its first bytes. A render failure before any output retains http.Error's text response.

Handler renders without a generated wrapper and does not use dot as a tag, so dot may be arbitrary template data. The handler reuses dot across requests; dot and its callbacks must support concurrent execution.

Types

type A

type A struct{ HTMLInner }

An A renders an HTML anchor element with dynamic inner HTML.

One A value may back multiple live jaws.Element values. Its HTML getter is shared by those Elements and must be safe for their render, update and event calls.

func NewA

func NewA(innerHTML any) *A

NewA returns an anchor widget whose inner HTML is rendered from innerHTML.

innerHTML is passed to bind.MakeHTMLGetter; plain strings are trusted HTML.

func (*A) JawsRender

func (u *A) JawsRender(elem *jaws.Element, w io.Writer, params []any) error

JawsRender renders ui as an HTML anchor element.

type Button

type Button struct{ HTMLInner }

Button renders an HTML button element with dynamic inner HTML.

One Button value may back multiple live jaws.Element values. Its HTML getter is shared by those Elements and must be safe for their render, update and event calls.

func NewButton

func NewButton(innerHTML any) *Button

NewButton returns a button widget whose inner HTML is rendered from innerHTML.

innerHTML is passed to bind.MakeHTMLGetter; plain strings are trusted HTML.

func (*Button) JawsRender

func (u *Button) JawsRender(elem *jaws.Element, w io.Writer, params []any) error

JawsRender renders ui as an HTML button element.

type Checkbox

type Checkbox struct{ InputBool }

Checkbox renders an HTML checkbox input bound to a bool setter.

A Checkbox value must back at most one live jaws.Element. Construct distinct Checkbox values over the same setter to render one bound value more than once.

func NewCheckbox

func NewCheckbox(g bind.Setter[bool]) *Checkbox

NewCheckbox returns a checkbox input widget bound to g.

For writable use, g must provide the setter-derived dirty target described by Input.

func (*Checkbox) JawsRender

func (u *Checkbox) JawsRender(elem *jaws.Element, w io.Writer, params []any) error

JawsRender renders ui as an HTML checkbox input.

type Container

type Container struct {
	// contains filtered or unexported fields
}

Container renders an HTML element around a dynamic child collection.

Its outer tag and child provider define its identity. The provider's dynamic value must be comparable and equal to itself. Rebuilding an equal Container lets a parent retain its live Element. Keep application state containing a slice, map, or function behind a stable pointer and synchronize access to it.

Reconciliation affects direct children only. Reordering retained equal children preserves their Elements and nested subtrees. Nested containers whose children change need their own update. Child Element identity is scoped to its parent; moving a child definition between parents does not preserve its Element. Each child must render one direct DOM node carrying its Element's JaWS ID. Use NewTemplate for Template children so removal and ordering can target a wrapper.

Equal Container values may back multiple live Elements in one jaws.Request when the provider is safe for all calls and each child UI value reused across those Elements supports multiple live Elements. Use Container as a value; taking its address changes identity and is unsupported.

A typed-nil provider is called normally and must tolerate its nil receiver.

Example (RenderScoped)
package main

import (
	"bytes"
	"encoding/json"
	"fmt"

	"github.com/linkdata/jaws"
	"github.com/linkdata/jaws/lib/ui"
)

type exampleContainer []string

func (c exampleContainer) JawsContains(elem *jaws.Element) (contents []jaws.UI) {
	for _, item := range c {
		contents = append(contents, ui.NewSpan(item))
	}
	return
}

func main() {
	firstRows := exampleContainer{"one"}
	secondRows := exampleContainer{"two"}
	first := ui.NewContainer("div", &firstRows)
	second := ui.NewContainer("div", &secondRows)
	fmt.Println(first == second)

	var b bytes.Buffer
	enc := json.NewEncoder(&b)
	_ = enc.Encode([]string{"construct containers during render"})
	fmt.Print(b.String())

}
Output:
false
["construct containers during render"]

func NewContainer

func NewContainer(outerHTMLTag string, children jaws.Container) Container

NewContainer returns a Container that renders children inside outerHTMLTag.

func (Container) JawsRender

func (u Container) JawsRender(elem *jaws.Element, w io.Writer, params []any) error

JawsRender renders u as its configured container element.

If elem's widget state is occupied, JawsRender returns jaws.ErrElementStateClaimed without rendering.

func (Container) JawsUpdate

func (u Container) JawsUpdate(elem *jaws.Element)

JawsUpdate reconciles u's direct children.

If elem's widget state cannot be used, JawsUpdate reports jaws.ErrElementStateClaimed through jaws.Request.MustLog without calling the provider or queuing browser work.

type Date

type Date struct{ InputDate }

Date renders an HTML date input bound to a time value setter.

A Date value must back at most one live jaws.Element. Construct distinct Date values over the same setter to render one bound value more than once.

The control is date-only: a browser edit normalizes the bound time.Time to midnight UTC of the picked date, discarding time-of-day and location. See InputDate.JawsInput.

func NewDate

func NewDate(g bind.Setter[time.Time]) *Date

NewDate returns a date input widget bound to g.

For writable use, g must provide the setter-derived dirty target described by Input.

The widget is date-only; see InputDate.JawsInput for how a browser edit normalizes the bound time.Time to midnight UTC and which years round-trip.

func (*Date) JawsRender

func (u *Date) JawsRender(elem *jaws.Element, w io.Writer, params []any) error

JawsRender renders ui as an HTML date input.

type Div

type Div struct{ HTMLInner }

Div renders an HTML div element with dynamic inner HTML.

One Div value may back multiple live jaws.Element values. Its HTML getter is shared by those Elements and must be safe for their render, update and event calls.

func NewDiv

func NewDiv(innerHTML any) *Div

NewDiv returns a div widget whose inner HTML is rendered from innerHTML.

innerHTML is passed to bind.MakeHTMLGetter; plain strings are trusted HTML.

func (*Div) JawsRender

func (u *Div) JawsRender(elem *jaws.Element, w io.Writer, params []any) error

JawsRender renders ui as an HTML div element.

type HTMLInner

type HTMLInner struct {
	// HTMLGetter returns the trusted inner HTML to render and update.
	HTMLGetter bind.HTMLGetter
}

HTMLInner is a reusable base for widgets that render as `<tag>inner</tag>`.

HTMLInner retains no Element-specific state. A widget embedding it may back multiple live jaws.Element values when its HTMLGetter is also safe for those Elements' render, update and event calls.

func (*HTMLInner) JawsUpdate

func (u *HTMLInner) JawsUpdate(elem *jaws.Element)

JawsUpdate updates the rendered inner HTML.

Unlike the typed input widgets, which dedup against a stored last value, HTMLInner keeps no last-rendered value and re-sends the inner HTML on every update; mark the jaws.Element dirty only when the content has actually changed.

type Img

type Img struct{ bind.Getter[string] }

Img renders an HTML img element whose src is read from a string getter.

The getter-derived src takes precedence over any src attribute passed as a render param or returned by the getter's jaws.InitialHTMLAttrHandler.

One Img value may back multiple live jaws.Element values. Its getter is shared by those Elements and must be safe for their render, update and event calls.

func NewImg

func NewImg(g bind.Getter[string]) *Img

NewImg returns an img widget whose src attribute is read from g.

func (*Img) JawsRender

func (u *Img) JawsRender(elem *jaws.Element, w io.Writer, params []any) (err error)

JawsRender renders ui as an HTML img element.

func (*Img) JawsUpdate

func (u *Img) JawsUpdate(elem *jaws.Element)

JawsUpdate updates the src attribute.

Like the other display widgets and unlike the typed inputs, Img keeps no last-rendered value and re-sends src on every update; mark the jaws.Element dirty only when src has actually changed.

type Input

type Input struct {

	// Last holds widget-specific state used to filter redundant browser updates.
	// Widget implementations own this cache; callers must not modify it.
	Last atomic.Value
	// contains filtered or unexported fields
}

Input stores common state for interactive input widgets.

An Input value provides a widget-owned update cache and dirty target for one live jaws.Element. A widget embedding Input must therefore back at most one live Element. To render the same bound state more than once, construct distinct widgets that share the setter.

For post-set reconciliation, a writable setter must expose at least one stable, usable tag through jaws.Element.ApplyGetter. bind.New exposes its backing value pointer.

After bind.Setter.JawsSet returns a result that does not match jaws.ErrValueUnchanged, Input dirties the setter-derived tag so the server value can reconcile rejected or normalized browser input. Tags supplied as render parameters register the Element but do not replace that dirty target. Without a valid setter-derived target, automatic reconciliation does not occur.

A completed native form reset changes browser state without an input/change event, so it does not update the Go binding. Reset authoritative Go values from a JaWS-handled button with type="button", then dirty their tags.

type InputBool

type InputBool struct {
	Input
	bind.Setter[bool]
}

InputBool is the reusable base for boolean input widgets.

A widget embedding InputBool must back at most one live jaws.Element.

func (*InputBool) JawsInput added in v0.401.0

func (u *InputBool) JawsInput(elem *jaws.Element, value string) (err error)

JawsInput stores a browser-side bool input value.

func (*InputBool) JawsUpdate

func (u *InputBool) JawsUpdate(elem *jaws.Element)

JawsUpdate updates the input value when the bound bool value changes.

type InputDate

type InputDate struct {
	Input
	bind.Setter[time.Time]
}

InputDate is the reusable base for date input widgets.

A widget embedding InputDate must back at most one live jaws.Element.

The control is date-only. Rendering shows the calendar date in the bound value's own location, but a browser edit normalizes the bound time.Time to midnight UTC of the picked date, and only years 1 through 9999 round-trip; see InputDate.JawsInput.

func (*InputDate) JawsInput added in v0.401.0

func (u *InputDate) JawsInput(elem *jaws.Element, value string) (err error)

JawsInput stores a browser-side date input value.

An empty value maps to the zero time.Time, which renders as "0001-01-01". Non-empty values are calendar dates (YYYY-MM-DD). time.Parse resolves them to midnight UTC, so the stored time.Time drops any time-of-day and time.Location the previously bound value carried. In a non-UTC deployment the stored instant therefore shifts by the zone offset, and because time.Time inequality includes the location, re-selecting the same date still reports a change and broadcasts it. Bind a date whose clock and zone are irrelevant, or keep your bound values at midnight UTC to match.

Only years 1 through 9999 round-trip. Those render as four digits, which is what the fixed-width "2006-01-02" layout parses back. A bound year of 10000 or more renders with five or more digits, because time.Time.Format widens the year field, but time.Parse then rejects the extra digit; that edit returns a parse error and leaves the last accepted value in place instead of updating the bound value. Keep bound years within 1..9999.

func (*InputDate) JawsUpdate

func (u *InputDate) JawsUpdate(elem *jaws.Element)

JawsUpdate updates the input value when the bound date value changes.

type InputText

type InputText struct {
	Input
	bind.Setter[string]
}

InputText is the reusable base for string input widgets.

A widget embedding InputText must back at most one live jaws.Element.

func (*InputText) JawsInput added in v0.401.0

func (u *InputText) JawsInput(elem *jaws.Element, value string) (err error)

JawsInput stores a browser-side string input value.

func (*InputText) JawsUpdate

func (u *InputText) JawsUpdate(elem *jaws.Element)

JawsUpdate updates the input value when the bound string value changes.

type IsJsVar

type IsJsVar interface {
	bind.RWLocker
	jaws.UI
	jaws.InputHandler
	PathSetter
}

IsJsVar is implemented by JaWS UI values that bind a Go value to a browser-side JavaScript variable.

type JsVar

type JsVar[T any] struct {
	bind.RWLocker
	Ptr         *T            // bound Go value
	ClientCheck JsVarCheck[T] // optional check for generic browser writes; configure before first use
	// contains filtered or unexported fields
}

JsVar binds a Go value to a named JavaScript variable in the browser.

A JsVar is request-scoped and must not be rendered by more than one jaws.Request. Within that request, it must back at most one live jaws.Element. Construct a fresh JsVar for each binding, either directly while rendering or through JsVarMaker. Distinct JsVar values may use the same locker and Ptr to expose synchronized application state to multiple Elements or requests.

JsVar is intended for JSON-marshalable state shared with application JavaScript. The browser binding reads and writes the window property named when the JsVar is rendered. Existing application variables are therefore valid bindings. Do not use a browser-owned property such as window.name, or a global owned by unrelated code.

Browser JSON numbers use JavaScript Number values. Signed and unsigned integers outside -9007199254740991 through 9007199254740991 may be rounded in a snapshot or broadcast; a later browser write may store the rounded value in Go.

Represent exact wide integers as built-in string fields and convert them explicitly. Browser code must convert BigInt values back to strings before calling jawsVar. A json:",string" tag is not generic round-trip support; use a built-in string field or a PathSetter.

JSON-marshalable describes server-to-browser values. Generic browser writes decode into any and assign by path; they do not unmarshal directly into T or invoke destination custom unmarshaling. Consequently, time.Time, []byte, and maps with non-string keys are not round-trip writable by the generic setter. Use a browser-facing DTO compatible with the generic setter, or implement PathSetter to parse and validate the decoded value.

Generic array and slice path components must be canonical JavaScript array indices representable as int: "0" or ASCII decimal digits without a leading zero, at most 4294967294. A component that violates these rules produces an error matching github.com/linkdata/jq.ErrPathNotFound when traversal reaches an array or slice. String-keyed map entries are matched exactly. Struct path components and map-to-struct keys follow the default field-selection rules of encoding/json. An exact json:"-" tag excludes an otherwise selected exported field. For a non-promoting field, a valid nonempty JSON tag name is used verbatim, while an absent, empty, or invalid name falls back to the Go field name; json:"-," therefore names the field "-". Ambiguous fields are absent from the path namespace.

An anonymous struct without a valid explicit JSON name contributes its promoted fields directly and does not add its Go type name as a component: use "value", not "Inner.value", or give the anonymous field an explicit tag to create a nested path. Promotion reaches exported fields through unexported embedded structs. An explicitly named unexported anonymous struct is not itself a readable or writable endpoint or writable map-to-struct key, but longer paths can reach its exported fields.

Reads and generic writes that traverse a nil pointer produce an error matching github.com/linkdata/jq.ErrPathNotFound, and generic writes do not allocate the pointer. Empty components are ignored, and both "" and "." address the root.

While the WebSocket is open, jawsVar sends one complete message per matching live binding, subject to jaws.Request.ServeHTTP's inbound limit. JsVar.ClientCheck runs only after receipt and cannot enforce that limit. Use a separate upload endpoint for large data.

The variable name and browser-side jawsVar paths must be application-controlled. The browser rejects exact "__proto__" path components; put user data in values, not names or paths.

Multiple bindings may share a name. The name is a single browser window property, and a browser-initiated write to it is delivered to every live binding of that name; a removed binding stops receiving writes. This lets a subtree re-render replace a binding, lets several requests expose the same application-owned global, and lets one browser value fan out to several independent Go bindings.

When bindings sharing a name also share a mutable backing object graph, a browser write applies to that shared state once per binding. Those bindings must use the same locker. Do not expose that state through several simultaneously rendered bindings when its PathSetter is not idempotent (for example one that appends).

Unlike most JaWS UI values, a JsVar is a bidirectional channel and does not imply that the Go value is always authoritative. Browser and Go updates may each carry a complete value or individual paths. Any desired ownership, conflict, or merge policy is the application's responsibility.

When Ptr is non-nil, JsVar.JawsRender serializes a snapshot of the bound Go value to initialize the browser variable. A browser call to the JavaScript jawsVar function sends only while its WebSocket is open; an earlier call is not queued for later transmission. JsVar.JawsSet and JsVar.JawsSetPath broadcast only to matching active requests, so an update is not replayed to a page between its initial render and its broadcast subscription. Applications that require the two sides to converge after either can change the value during that interval must reconcile it explicitly.

It is safe for concurrent use when the locker passed to NewJsVar is safe for concurrent use. Concurrent writes are applied one at a time. Any broadcasts they produce preserve the order in which the writes modify the bound value. This concurrency guarantee does not permit one JsVar to be shared between requests.

Rendering and write broadcasts invoke JSON marshalers while the locker passed to NewJsVar is held. Custom marshaling callbacks reached in either case, including MarshalJSON and MarshalText, must not acquire that locker or re-enter the JsVar.

A JsVar must not be copied after first use.

SECURITY: a JsVar is client-writable. Incoming browser "set" messages are applied by path to the bound value. If the bound value implements PathSetter, its JawsSetPath validates and applies the change. Otherwise github.com/linkdata/jq.Set can write any exported field addressable by the generic path rules above and append one element per message to a slice.

There is no default cumulative size bound. Set JsVar.ClientCheck before first use to validate each tentative generic browser update. JSONSizeCheck provides an exact serialized-size limit. Every client-writable binding that can mutate the same backing object graph must share synchronization and use an equivalent checking policy. An unchecked or less restrictive binding can otherwise commit first; another binding then sees an unchanged value and does not run its check. A ClientCheck does not run for rendering, programmatic writes, invalid or unchanged writes, or values implementing PathSetter. It is an acceptance gate, not a monitor that proves the current value always satisfies an invariant. A check that uses github.com/linkdata/jq.Get cannot inspect an explicitly named unexported anonymous struct at its own endpoint; it must inspect a longer exported-field path or the tentative Go value directly.

A size check does not prevent a client from setting individual exported fields. When only some fields or paths should be client-writable, implement PathSetter on the bound value to allow-list paths and bound lengths.

Rejecting a browser write rolls back Go state without changing the value that the browser already assigned before sending it. Except for ErrJsVarTooLarge, which aborts the associated request when present, the application must resynchronize the browser if it requires immediate convergence after rejection. See the Node type in github.com/linkdata/jawstree for an example that restricts client writes to a single boolean field.

Example (PathSetter)
package main

import (
	"errors"
	"fmt"
	"sync"

	"github.com/linkdata/jaws"
	"github.com/linkdata/jaws/lib/ui"
)

type examplePathState struct {
	Title string   `json:"title"`
	Items []string `json:"items"`
}

func (state *examplePathState) JawsSetPath(elem *jaws.Element, jsPath string, value any) error {
	if jsPath != "title" {
		return fmt.Errorf("%w: %s", ui.ErrIllegalJsVarPath, jsPath)
	}
	title, ok := value.(string)
	if !ok {
		return fmt.Errorf("title: %T", value)
	}
	if state.Title == title {
		return jaws.ErrValueUnchanged
	}
	state.Title = title
	return nil
}

func main() {
	var mu sync.Mutex
	state := examplePathState{Title: "old", Items: []string{"server-owned"}}
	jsv := ui.NewJsVar(&mu, &state)

	if err := jsv.JawsSetPath(nil, "title", "new"); err != nil {
		panic(err)
	}
	err := jsv.JawsSetPath(nil, "items.1", "blocked")
	fmt.Println(state.Title)
	fmt.Println(errors.Is(err, ui.ErrIllegalJsVarPath))

}
Output:
new
true

func NewJsVar

func NewJsVar[T any](l sync.Locker, v *T) *JsVar[T]

NewJsVar creates a JsVar over v protected by l.

The locker l must remain valid for the lifetime of the JsVar. The pointer v may be nil; reads then return the zero value, rendering omits the initial data, and writes return github.com/linkdata/jq.ErrInvalidReceiver. Create a fresh JsVar for each live jaws.Element; l and v may be shared by distinct request-scoped JsVar values. Use JsVarMaker when construction depends on the current request or the maker is stored in shared handler data.

func (*JsVar[T]) JawsGet

func (jsvar *JsVar[T]) JawsGet(elem *jaws.Element) (value T)

JawsGet returns the bound value.

func (*JsVar[T]) JawsGetPath

func (jsvar *JsVar[T]) JawsGetPath(elem *jaws.Element, jsPath string) (value any)

JawsGetPath returns the value at jsPath.

A path containing only empty components returns the same logical root value as JsVar.JawsGet. Lookup errors return nil and are logged on elem when possible. A nil result therefore does not distinguish a lookup failure from a successfully resolved nil value.

func (*JsVar[T]) JawsGetTag

func (jsvar *JsVar[T]) JawsGetTag() any

JawsGetTag returns the dirty tag resolved by JsVar.JawsRender, or nil.

A failed JawsRender may still resolve the tag. Once non-nil, it follows the stable-identity contract of github.com/linkdata/jaws/lib/tag.TagGetter. Using the JsVar as a tag while nil registers no keys; later resolution is not retroactive.

It is safe for concurrent use.

func (*JsVar[T]) JawsInput added in v0.401.0

func (jsvar *JsVar[T]) JawsInput(elem *jaws.Element, value string) (err error)

JawsInput applies a browser-side JavaScript variable update.

See JsVar for generic write representation limits.

For a generic path write, a non-nil JsVar.ClientCheck validates the complete tentative state before it is committed. The check runs while the JsVar write lock is held. It must only inspect value: it must not mutate it, re-enter this JsVar, call a jq setter on it, or retain references into a rejected tentative value. An error rejects the write atomically, and a panic rolls the write back before it propagates.

A ClientCheck error matching ErrJsVarTooLarge aborts the associated request, when elem has one, after the JsVar locks have been released. Other check errors reject the write without aborting a request. ClientCheck is not invoked for invalid or unchanged writes, values implementing PathSetter, or programmatic JsVar.JawsSetPath calls.

ClientCheck validates the tentative Go state. An accepted broadcast still carries the decoded browser value, which may differ after jq conversion or map-to-struct field selection; see JsVarCheck.

func (*JsVar[T]) JawsRender

func (jsvar *JsVar[T]) JawsRender(elem *jaws.Element, w io.Writer, params []any) (err error)

JawsRender writes the hidden element that seeds and routes the JavaScript variable.

params[0] must be a valid JsVar name. An invalid name returns ErrIllegalJsVarName without writing markup, but still applies any bound-value tag and handlers, invokes its initial-attribute hook, and adds the JsVar handler.

A name may be bound by more than one live binding; see JsVar for how a browser write is delivered to every live binding of the name.

The serialized value is a render-time snapshot. See JsVar for the synchronization semantics between rendering and the WebSocket subscription.

The bound value's github.com/linkdata/jaws/lib/tag.TagGetter JawsGetTag runs with the JsVar write lock held and must not acquire that lock or re-enter the JsVar.

jaws.InitialHTMLAttrHandler.JawsInitialHTMLAttr runs without the JsVar lock and may acquire the locker passed to NewJsVar.

func (*JsVar[T]) JawsSet

func (jsvar *JsVar[T]) JawsSet(elem *jaws.Element, value T) (err error)

JawsSet replaces the root value and broadcasts the change.

It has the same delivery and marshaling semantics as JsVar.JawsSetPath.

func (*JsVar[T]) JawsSetPath

func (jsvar *JsVar[T]) JawsSetPath(elem *jaws.Element, jsPath string, value any) (err error)

JawsSetPath sets the value at jsPath and broadcasts the change when possible. It is a programmatic server-side write, so it does not invoke JsVar.ClientCheck.

See JsVar for generic path rules.

A nil elem changes the bound value without broadcasting. A set before this JsVar has acquired a dirty tag from rendering also produces no broadcast; its initial render seeds the value via the data-jawsdata attribute.

The broadcast reaches matching active requests only. It is not replayed to a page between its initial render and its broadcast subscription; see JsVar for the synchronization model.

The browser receives the JSON encoding of value, not a re-encoding of the destination field after assignment. Applications using an encoded representation such as decimal strings must pass that representation in value.

If marshaling a broadcast value fails, JawsSetPath returns the error after applying the write. It does not roll back the value, queue a broadcast, or call SetPather.JawsPathSet.

When a write produces a broadcast, value is marshaled while the application locker is held. Custom marshaling callbacks reachable from value, including MarshalJSON and MarshalText, must not acquire that locker or re-enter the JsVar.

func (*JsVar[T]) JawsUpdate

func (jsvar *JsVar[T]) JawsUpdate(elem *jaws.Element)

JawsUpdate is a no-op because updates are broadcast by path setters.

Dirtying a JsVar therefore does not resend its root value. Use JsVar.JawsSet or JsVar.JawsSetPath, together with the application's synchronization policy, to send changes.

type JsVarCheck added in v0.700.0

type JsVarCheck[T any] = func(value *T, jsPath string) error

JsVarCheck validates a tentative browser update to a JsVar.

JsVarCheck is a type alias so a value of a defined function type with this signature can be assigned to JsVar.ClientCheck without explicit conversion.

The value contains the complete tentative state, and jsPath is the original browser-supplied jq path. The generic setter ignores empty components, so use jsPath only as an inspection hint; implement PathSetter to allow-list paths. See JsVar for generic path rules.

A nil error accepts the update. A non-nil error rejects it atomically and is returned unchanged, except that an error matching ErrJsVarTooLarge is returned as that sentinel after cancelling the associated request, when one is present. A panic rejects the update before continuing unchanged.

The check validates tentative Go state only. A broadcast carries the decoded browser value rather than reading the stored path back, so jq conversions or ignored map-to-struct entries can make the peer value differ from the state inspected here. Use PathSetter when peer-visible input also needs validation.

The check runs while the locker passed to NewJsVar is write-locked. It may inspect or marshal value, but it must not mutate it, acquire that locker, re-enter the JsVar, call a jq setter on the value, or retain references into a rejected tentative value. Any custom marshaling callback it invokes, including MarshalJSON or MarshalText, has the same restrictions. The check must not return or wrap jaws.ErrEventUnhandled, which has handler-dispatch semantics.

func JSONSizeCheck added in v0.700.0

func JSONSizeCheck[T any](maxBytes int) (check JsVarCheck[T])

JSONSizeCheck returns a check that limits the JSON encoding of value.

The check accepts an encoding whose length is exactly maxBytes and rejects a larger encoding with ErrJsVarTooLarge. A marshaling failure also matches ErrJsVarTooLarge and retains the marshaling error in its error chain. A non-positive maxBytes disables checking and returns nil.

JSONSizeCheck marshals the complete value after every tentative change. Its time and allocation cost depend on the whole value and its marshaling behavior; map-key sorting and custom marshalers can add further cost. It bounds the encoding, not Go heap memory: custom JSON marshalers, omitted fields, aliases, slice capacity, and object overhead may make the two differ. Use a domain-specific JsVarCheck unless the JSON representation faithfully includes all state a client can grow.

Example
package main

import (
	"sync"

	"github.com/linkdata/jaws/lib/ui"
)

func main() {
	type clientState struct {
		Items []string `json:"items"`
	}

	var mu sync.Mutex
	state := clientState{}
	jsv := ui.NewJsVar(&mu, &state)
	jsv.ClientCheck = ui.JSONSizeCheck[clientState](1 << 20)

	_ = jsv // render this request-scoped binding normally
}

type JsVarMaker

type JsVarMaker interface {
	JawsMakeJsVar(rq *jaws.Request) (value IsJsVar, err error)
}

JsVarMaker creates a request-scoped JavaScript variable binding.

JawsMakeJsVar must return a fresh IsJsVar for each call. The returned value is scoped to rq and one live jaws.Element. Bindings may share synchronized backing state, but the binding values themselves must remain distinct.

type Label

type Label struct{ HTMLInner }

Label renders an HTML label element with dynamic inner HTML.

One Label value may back multiple live jaws.Element values. Its HTML getter is shared by those Elements and must be safe for their render, update and event calls.

func NewLabel

func NewLabel(innerHTML any) *Label

NewLabel returns a label widget whose inner HTML is rendered from innerHTML.

innerHTML is passed to bind.MakeHTMLGetter; plain strings are trusted HTML.

func (*Label) JawsRender

func (u *Label) JawsRender(elem *jaws.Element, w io.Writer, params []any) error

JawsRender renders ui as an HTML label element.

type Li

type Li struct{ HTMLInner }

Li renders an HTML list item with dynamic inner HTML.

One Li value may back multiple live jaws.Element values. Its HTML getter is shared by those Elements and must be safe for their render, update and event calls.

func NewLi

func NewLi(innerHTML any) *Li

NewLi returns a list item widget whose inner HTML is rendered from innerHTML.

innerHTML is passed to bind.MakeHTMLGetter; plain strings are trusted HTML.

func (*Li) JawsRender

func (u *Li) JawsRender(elem *jaws.Element, w io.Writer, params []any) error

JawsRender renders ui as an HTML list item.

type Number

type Number struct {
	Input
	// contains filtered or unexported fields
}

Number renders an HTML number input.

A Number value must back at most one live jaws.Element. Construct distinct Number values over the same source to render one bound value more than once. Construct a Number with NewNumber; using its zero value as a widget panics.

Editable Numbers send edits on the browser's change event. Pending edits remain browser-local until then and may be replaced by server or ancestor renders.

func NewNumber

func NewNumber[T Numeric](source bind.Getter[T]) *Number

NewNumber returns a number input widget bound to source.

Predeclared and named integer and floating-point types are parsed and formatted at their own width. Integer sources use base-10 integer syntax. If source's dynamic type also implements bind.Setter, the input is editable; rendering fails unless source exposes the stable, usable tag described by Input. A getter-only source renders read-only.

A non-finite bound floating-point value cancels the jaws.Request with a cause matching jaws.ErrValueNotFinite.

func (*Number) JawsInput added in v0.700.0

func (u *Number) JawsInput(elem *jaws.Element, text string) error

JawsInput settles a browser-side number edit.

Empty, malformed, non-finite, and unrepresentable browser values are rejected without calling the setter or returning an error. The canonical value is restored by updating only the originating Element. Accepted text is reconciled through the binding's formatter, including when the setter returns jaws.ErrValueUnchanged. Getter-only Numbers ignore browser input.

func (*Number) JawsRender

func (u *Number) JawsRender(elem *jaws.Element, w io.Writer, params []any) (err error)

JawsRender renders the Number as an HTML number input.

func (*Number) JawsUpdate added in v0.700.0

func (u *Number) JawsUpdate(elem *jaws.Element)

JawsUpdate reconciles the input with its canonical source value.

type Numeric added in v0.700.0

type Numeric interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
		~float32 | ~float64
}

Numeric is an integer or floating-point type supported by Number and Range.

Named types with one of these underlying types are included.

type Object added in v0.400.0

type Object interface {
	bind.HTMLGetter
	tag.TagGetter
	jaws.ClickHandler
	jaws.ContextMenuHandler
	jaws.InitialHTMLAttrHandler

	// Clicked adds fn as the newest click hook and returns the resulting [Object].
	Clicked(fn ObjectClickedHook) (newobj Object)

	// ContextMenu adds fn as the newest context-menu hook and returns the
	// resulting [Object].
	ContextMenu(fn ObjectContextMenuHook) (newobj Object)

	// InitialHTMLAttr adds fn as the newest initial-attribute hook and returns the
	// resulting [Object].
	InitialHTMLAttr(fn ObjectInitialHTMLAttrHook) (newobj Object)
}

Object is a chainable UI object.

Each call to Object.Clicked, Object.ContextMenu, or Object.InitialHTMLAttr returns a new chain node wrapping its receiver. Calls to jaws.ClickHandler.JawsClick and jaws.ContextMenuHandler.JawsContextMenu run the corresponding hooks from newest to oldest. Dispatch continues while the result matches jaws.ErrEventUnhandled according to errors.Is, including when wrapped, and stops at the first other result. If no hook handles the event, the invoked method returns an error matching jaws.ErrEventUnhandled. Each hook receives the node containing it as its Object argument; that node includes the hook and all older links, but no newer links.

jaws.InitialHTMLAttrHandler.JawsInitialHTMLAttr runs all initial-attribute hooks from newest to oldest. Their non-empty results are joined in that order with one space inserted between results.

The effective expanded tag set combines the non-nil tag contributions of every link, and adding a link preserves older links' contributions. The resulting Object remains subject to tag.TagGetter's initialization, stability, and concurrency requirements. Use tag.TagExpand to obtain flattened, validated keys.

func New added in v0.400.0

func New(innerHTML any) (obj Object)

New returns a new Object that renders innerHTML.

innerHTML is passed to bind.MakeHTMLGetter, which may or may not provide tags. Plain strings are trusted HTML.

type ObjectClickedHook added in v0.500.0

type ObjectClickedHook func(obj Object, elem *jaws.Element, click jaws.Click) (err error)

ObjectClickedHook handles a click event for an Object.

obj is the chain node containing the hook. See Object for composition semantics.

Unlike bind.ClickedHook, ObjectClickedHook receives an Object rather than a bind.Binder.

type ObjectContextMenuHook added in v0.500.0

type ObjectContextMenuHook func(obj Object, elem *jaws.Element, click jaws.Click) (err error)

ObjectContextMenuHook handles a context menu event for an Object.

obj is the chain node containing the hook. See Object for composition semantics.

type ObjectInitialHTMLAttrHook added in v0.500.0

type ObjectInitialHTMLAttrHook = func(obj Object, elem *jaws.Element) (s template.HTMLAttr)

ObjectInitialHTMLAttrHook provides attributes when an Object is initially rendered.

obj is the chain node containing the hook. See Object for composition semantics.

ObjectInitialHTMLAttrHook is a type alias so a value of a defined function type with this signature can be passed to Object.InitialHTMLAttr without explicit conversion.

type Option

type Option struct{ *named.Bool }

Option renders an HTML option element backed by a named.Bool.

The value attribute is always named.Bool.Name and takes precedence over a value attribute passed as a render param.

One Option value may back multiple live jaws.Element values. All of those Elements reflect the shared Bool.

func NewOption

func NewOption(nb *named.Bool) Option

NewOption returns an option widget backed by nb.

func (Option) JawsRender

func (u Option) JawsRender(elem *jaws.Element, w io.Writer, params []any) error

JawsRender renders ui as an HTML option element.

func (Option) JawsUpdate

func (u Option) JawsUpdate(elem *jaws.Element)

JawsUpdate updates the selected attribute.

type Password

type Password struct{ InputText }

Password renders an HTML password input bound to a string setter.

A Password value must back at most one live jaws.Element. Construct distinct Password values over the same setter to render one bound value more than once.

func NewPassword

func NewPassword(g bind.Setter[string]) *Password

NewPassword returns a password input widget bound to g.

For writable use, g must provide the setter-derived dirty target described by Input.

func (*Password) JawsRender

func (u *Password) JawsRender(elem *jaws.Element, w io.Writer, params []any) error

JawsRender renders ui as an HTML password input.

type PathSetter

type PathSetter interface {
	// JawsSetPath should set the JSON object member identified by jsPath to the given value.
	//
	// Browser writes pass [json.Unmarshal]'s generic any representation without
	// the raw JSON bytes. Programmatic [JsVar.JawsSetPath] calls pass the
	// caller-supplied value unchanged.
	//
	// A [JsVar] returns [ErrIllegalJsVarPath] before calling a PathSetter when
	// jsPath contains a protocol byte. Otherwise it passes jsPath unchanged and
	// does not apply generic jq validation. Successful broadcasts preserve jsPath
	// and the requested value.
	//
	// If the member is already the given value, it should return [jaws.ErrValueUnchanged].
	//
	// When a [JsVar]'s bound value (Ptr) implements PathSetter, the JsVar
	// delegates to it while holding the JsVar write lock. Such an
	// implementation must not lock or unlock the JsVar, nor call its locked
	// accessors such as [JsVar.JawsGet] or [JsVar.JawsSet].
	//
	// When an accepted update is broadcast, the JsVar marshals value before
	// releasing that write lock. Custom marshaling callbacks reachable from value,
	// including MarshalJSON and MarshalText, must not acquire the same locker or
	// re-enter the JsVar.
	//
	// If an implementation panics, the calling JsVar releases its write lock
	// before propagating the panic.
	JawsSetPath(elem *jaws.Element, jsPath string, value any) (err error)
}

PathSetter can set a nested JSON path value.

type Radio

type Radio struct{ InputBool }

Radio renders an HTML radio input bound to a bool setter.

A Radio value must back at most one live jaws.Element. Construct distinct Radio values over the same setter to render one bound value more than once.

Each Radio binds an independent boolean. An HTML radio group does not group its Go bindings: the browser reports the newly checked radio, not peers it unchecks. Use RequestWriter.RadioGroup with a single-select named.BoolArray and distinct named.Bool.Name values, or coordinated setters that clear peers together and dirty every changed binding.

func NewRadio

func NewRadio(g bind.Setter[bool]) *Radio

NewRadio returns a radio input widget bound to g.

For writable use, g must provide the setter-derived dirty target described by Input. See Radio for grouping semantics.

func (*Radio) JawsRender

func (u *Radio) JawsRender(elem *jaws.Element, w io.Writer, params []any) error

JawsRender renders ui as an HTML radio input.

type RadioElement

type RadioElement struct {
	// contains filtered or unexported fields
}

RadioElement renders the input and label elements for one radio option.

The underlying jaws.Element values are created lazily on the first call to RadioElement.Radio or RadioElement.Label, so options that a template never renders register no elements on the jaws.Request. Call each of Radio and Label at most once. Render Label only when Radio is also rendered: Label emits a for="..." referencing the radio's id, so a Label without its Radio points at an input that is absent from the document. The radio Element it created is still unregistered by the Template that owns it (see RequestWriter.RadioGroup) when that template next replaces its content. With no template owner it has no DOM node for a removal to report either, so it stays registered until the jaws.Request ends.

func (RadioElement) Label

func (re RadioElement) Label(params ...any) template.HTML

Label renders an HTML label element.

The generated for= attribute referencing the radio's id takes precedence over any for= passed in params: it is emitted first and the HTML parser keeps the first of duplicate attributes, so the label always targets its own radio.

Render errors are reported through jaws.Jaws.MustLog, which panics when no jaws.Jaws.Logger is configured.

func (RadioElement) Radio

func (re RadioElement) Radio(params ...any) template.HTML

Radio renders an HTML input element of type radio.

The group's generated name= attribute takes precedence over any name= passed in params: it is emitted first and the HTML parser keeps the first of duplicate attributes, preserving the invariant that every radio in the group shares the same request-scoped name.

Render errors are reported through jaws.Jaws.MustLog, which panics when no jaws.Jaws.Logger is configured.

type Range

type Range struct {
	Input
	// contains filtered or unexported fields
}

Range renders an HTML range input.

A Range value must back at most one live jaws.Element. Construct distinct Range values over the same source to render one bound value more than once. Construct a Range with NewRange; using its zero value as a widget panics. Editable Ranges send live browser input while their thumb moves.

func NewRange

func NewRange[T Numeric](source bind.Getter[T]) *Range

NewRange returns a range input widget bound to source.

Predeclared and named integer and floating-point types are parsed and formatted at their own width. Integer sources use base-10 integer syntax. If source's dynamic type also implements bind.Setter, the input is editable; rendering fails unless source exposes the stable, usable tag described by Input. A getter-only source renders disabled; when tagged, it continues to receive dirty-driven server updates.

A non-finite bound floating-point value cancels the jaws.Request with a cause matching jaws.ErrValueNotFinite.

Range emits no min, max, or step attributes, so the browser defaults apply. Supply them as render parameters when those defaults do not fit the source domain. The browser may clamp or round the displayed value; a browser-adjusted value reaches the setter only on a later input event and only when representable by T.

func (*Range) JawsInput added in v0.700.0

func (u *Range) JawsInput(elem *jaws.Element, text string) error

JawsInput accepts or rejects a browser-side range value.

Text that is malformed or cannot be represented by the source type is rejected without calling the setter or returning an error. Accepted and rejected text is reconciled with the binding's formatter. Rejection and jaws.ErrValueUnchanged update only the originating Element. Getter-only Ranges ignore browser input.

func (*Range) JawsRender

func (u *Range) JawsRender(elem *jaws.Element, w io.Writer, params []any) (err error)

JawsRender renders the Range as an HTML range input.

func (*Range) JawsUpdate added in v0.700.0

func (u *Range) JawsUpdate(elem *jaws.Element)

JawsUpdate reconciles the range with its canonical source value.

type RequestWriter

type RequestWriter struct {
	*jaws.Request
	io.Writer
	// contains filtered or unexported fields
}

RequestWriter combines a jaws.Request with an io.Writer while rendering.

func (RequestWriter) A

func (rw RequestWriter) A(innerHTML any, params ...any) error

A renders an HTML anchor element. A plain string innerHTML is trusted HTML; see NewA and bind.MakeHTMLGetter to pass untrusted input safely.

func (RequestWriter) Button

func (rw RequestWriter) Button(innerHTML any, params ...any) error

Button renders an HTML button element. A plain string innerHTML is trusted HTML; see NewButton and bind.MakeHTMLGetter to pass untrusted input safely.

func (RequestWriter) Checkbox

func (rw RequestWriter) Checkbox(value any, params ...any) error

Checkbox renders an HTML checkbox input.

func (RequestWriter) Container

func (rw RequestWriter) Container(outerHTMLTag string, children jaws.Container, params ...any) error

Container renders children inside outerHTMLTag.

func (RequestWriter) Date

func (rw RequestWriter) Date(value any, params ...any) error

Date renders an HTML date input.

The control is date-only: a browser edit normalizes the bound time.Time to midnight UTC of the picked date, discarding time-of-day and location. See InputDate.JawsInput.

func (RequestWriter) Div

func (rw RequestWriter) Div(innerHTML any, params ...any) error

Div renders an HTML div element. A plain string innerHTML is trusted HTML; see NewDiv and bind.MakeHTMLGetter to pass untrusted input safely.

func (RequestWriter) Get

func (rw RequestWriter) Get(key string) (value any)

Get calls jaws.Request.Get.

func (RequestWriter) HeadHTML

func (rw RequestWriter) HeadHTML() error

HeadHTML calls jaws.Request.HeadHTML.

func (RequestWriter) Img

func (rw RequestWriter) Img(imageSrc any, params ...any) error

Img renders an HTML img element.

func (RequestWriter) Initial

func (rw RequestWriter) Initial() *http.Request

Initial returns the initial http.Request.

func (RequestWriter) JsVar

func (rw RequestWriter) JsVar(jsvarName string, jsvar any, params ...any) (err error)

JsVar binds a JsVar to a named JavaScript variable.

jsvarName identifies a property on the browser window. It should be owned by the application because the binding initializes and updates its value.

See JsVar for the bidirectional binding and synchronization semantics, including how a name shared by several live bindings is routed.

It returns ErrIllegalJsVarName if jsvarName is invalid or reserved.

A directly supplied JsVar must be scoped to rw.Request. You can instead pass a JsVarMaker, which is useful when the maker is stored in handler or template data shared by multiple requests.

func (RequestWriter) Label

func (rw RequestWriter) Label(innerHTML any, params ...any) error

Label renders an HTML label element. A plain string innerHTML is trusted HTML; see NewLabel and bind.MakeHTMLGetter to pass untrusted input safely.

func (RequestWriter) Li

func (rw RequestWriter) Li(innerHTML any, params ...any) error

Li renders an HTML list item. A plain string innerHTML is trusted HTML; see NewLi and bind.MakeHTMLGetter to pass untrusted input safely.

func (RequestWriter) NewElement added in v0.700.0

func (rw RequestWriter) NewElement(ui jaws.UI) *jaws.Element

NewElement creates a new jaws.Element for ui.

It has the same ownership and multiplicity requirements as jaws.Request.NewElement. An Element created through the RequestWriter passed to a Template execution belongs to that Template.

func (RequestWriter) NewUI added in v0.500.0

func (rw RequestWriter) NewUI(ui jaws.UI, params ...any) (err error)

NewUI creates an element for ui and renders it to the underlying writer.

The ui value must satisfy the ownership and live-Element multiplicity requirements documented by jaws.UI.

func (RequestWriter) Number

func (rw RequestWriter) Number(value any, params ...any) error

Number renders an HTML number input for value.

Numeric values and getter-only bind.Getter sources render read-only; bind.Setter sources render editable. It panics for any other value.

func (RequestWriter) Password

func (rw RequestWriter) Password(value any, params ...any) error

Password renders an HTML password input.

func (RequestWriter) Radio

func (rw RequestWriter) Radio(value any, params ...any) error

Radio renders an HTML radio input.

See Radio for grouping semantics.

func (RequestWriter) RadioGroup

func (rw RequestWriter) RadioGroup(nba *named.BoolArray) (rel []RadioElement)

RadioGroup returns a RadioElement for each value in nba.

Elements are created lazily as they are rendered; see RadioElement. Every rendered radio in the group shares a name derived from the first created radio Element's request-scoped jaws.Jid.

Use a single-select named.BoolArray with distinct named.Bool.Name values. Multi-select arrays and duplicate names are incompatible with native radio semantics. Separately bound Radio widgets are not grouped server-side by their HTML name.

Call RadioGroup from the Template that renders the returned RadioElement values; do not pass them into a nested wrapped Template for rendering.

func (RequestWriter) Range

func (rw RequestWriter) Range(value any, params ...any) error

Range renders an HTML range input for value.

Numeric values and getter-only bind.Getter sources render disabled; bind.Setter sources render editable. It panics for any other value. Supply min, max, and step attributes in params when the browser defaults do not cover the source's domain. For example:

rw.Range(floatBinder, `min="0"`, `max="200"`, `step="0.5"`)

func (RequestWriter) Register

func (rw RequestWriter) Register(updater jaws.Updater, params ...any) jid.Jid

Register binds an updater to otherwise static template-authored HTML.

Register is an advanced escape hatch primarily for updating HTML whose markup is written by the surrounding template. The registered HTML is intended to contain no JaWS widgets. Render widgets through RequestWriter.NewUI or their corresponding RequestWriter helpers instead.

Register makes no compatibility guarantees for passing a standard widget as updater or placing JaWS widgets inside the registered HTML. Their behavior in either position is unspecified.

The returned jid.Jid must be the element's id. Register never calls jaws.Renderer.JawsRender, so updater must work without render-time initialization.

Register tags the Element with updater, applies tag and event-handler params, attaches event-handler methods implemented by updater, and invokes jaws.Updater.JawsUpdate once for the initial browser state. Updater handlers are tried only after applicable param handlers return jaws.ErrEventUnhandled. HTML attribute params have no effect; write attributes in the template.

The updater must be comparable at runtime, equal to itself, and usable as a tag. A typed nil is invoked normally and must tolerate its nil receiver. The same updater may back multiple live Elements only when it supports that use without retaining Element-specific state on the shared value. If shared across requests, it must be safe for concurrent use.

A surrounding Template owns and cleans up the registered Element. Outside a Template, it remains registered until explicitly deleted, DOM removal is reported, or its jaws.Request ends; always emit the returned Jid.

The returned Jid is suitable for including as an HTML id attribute:

<div id="{{$.Register .MyUpdater}}">...</div>

func (RequestWriter) Select

func (rw RequestWriter) Select(handler named.SelectHandler, params ...any) error

Select renders a single-selection HTML select element.

HTML attribute params are applied to the select element, but the multiple attribute is unsupported because Select stores one selected option value. See Select for handler requirements and native reset semantics.

func (RequestWriter) Session

func (rw RequestWriter) Session() *jaws.Session

Session returns the request's jaws.Session, or nil.

func (RequestWriter) Set

func (rw RequestWriter) Set(key string, value any)

Set calls jaws.Request.Set.

func (RequestWriter) Span

func (rw RequestWriter) Span(innerHTML any, params ...any) error

Span renders an HTML span element. A plain string innerHTML is trusted HTML; see NewSpan and bind.MakeHTMLGetter to pass untrusted input safely.

func (RequestWriter) TailHTML

func (rw RequestWriter) TailHTML() error

TailHTML writes optional HTML code at the end of the page's BODY section that will immediately apply updates made during initial rendering.

func (RequestWriter) Tbody

func (rw RequestWriter) Tbody(children jaws.Container, params ...any) error

Tbody renders children in an HTML tbody element.

func (RequestWriter) Td

func (rw RequestWriter) Td(innerHTML any, params ...any) error

Td renders an HTML table cell. A plain string innerHTML is trusted HTML; see NewTd and bind.MakeHTMLGetter to pass untrusted input safely.

func (RequestWriter) Template

func (rw RequestWriter) Template(outerHTMLTag, name string, dot any, params ...any) error

Template renders the named partial template with dot exposed as With.Dot.

The generated outerHTMLTag wrapper owns the JaWS ID, HTML attributes in params, and attributes returned by dot when it implements jaws.InitialHTMLAttrHandler. Attributes in params take precedence when dot returns an attribute with the same name. An empty outerHTMLTag defaults to "div". See NewTemplate.

func (RequestWriter) Text

func (rw RequestWriter) Text(value any, params ...any) error

Text renders an HTML text input.

func (RequestWriter) Textarea

func (rw RequestWriter) Textarea(value any, params ...any) error

Textarea renders an HTML textarea.

See Textarea for the browser-to-server message-size limit.

func (RequestWriter) Tr

func (rw RequestWriter) Tr(innerHTML any, params ...any) error

Tr renders an HTML table row. A plain string innerHTML is trusted HTML; see NewTr and bind.MakeHTMLGetter to pass untrusted input safely.

func (RequestWriter) Write

func (rw RequestWriter) Write(p []byte) (n int, err error)

Write marks render activity through jaws.Request.MarkWritten before writing p to the underlying writer.

type Select

type Select struct {
	// contains filtered or unexported fields
}

Select renders a single-selection HTML select element.

Its handler supplies the options and represents the selection as a string. Option values must be non-empty. A string that matches no option value represents no selection. named.BoolArray is the standard handler and requires non-empty named.Bool.Name values.

The handler's dynamic value defines Select's identity and must be comparable and equal to itself. Rebuilding with an equal handler lets a parent retain its live Element. Keep application state containing a slice, map, or function behind a stable pointer and synchronize access to it.

Equal Select values may back multiple live jaws.Element values in one jaws.Request when the handler is safe for all calls and each option UI value reused across those Elements supports multiple live Elements. Use Select as a value; taking its address changes identity and is unsupported.

A typed-nil handler is called normally and must tolerate its nil receiver.

Select supports one selected option; a multiple select is unsupported. A completed native form reset changes browser state without an input/change event, so it does not update the Go binding. Reset the authoritative selection from a JaWS-handled button with type="button", then dirty its tag.

func NewSelect

func NewSelect(handler named.SelectHandler) Select

NewSelect returns a single-selection Select backed by handler.

See Select for handler requirements and native reset semantics.

func (Select) JawsInput added in v0.401.0

func (u Select) JawsInput(elem *jaws.Element, value string) (err error)

JawsInput stores one browser-side selected option value.

A nil-interface handler is a no-op; a typed-nil handler is called normally. If no render-derived dirty tag is available, Select performs no additional dirtying after calling the handler.

func (Select) JawsRender

func (u Select) JawsRender(elem *jaws.Element, w io.Writer, params []any) (err error)

JawsRender renders u as an HTML select element.

On success, it queues the selected value after the options.

func (Select) JawsUpdate

func (u Select) JawsUpdate(elem *jaws.Element)

JawsUpdate reconciles the child options and then queues the selected value.

After a non-contended reconciliation returns, it queues the selected value. State contention suppresses both operations.

type SetPather

type SetPather interface {
	// JawsPathSet notifies that a JSON object member identified by jsPath has been set
	// to the given value and the change has been queued for broadcast.
	//
	// Unlike [PathSetter.JawsSetPath], a [JsVar] calls this after releasing
	// its lock, so locking the JsVar or calling its locked accessors is
	// allowed here.
	JawsPathSet(elem *jaws.Element, jsPath string, value any)
}

SetPather is notified after a nested JSON path value has been set and broadcast.

type Span

type Span struct{ HTMLInner }

Span renders an HTML span element with dynamic inner HTML.

One Span value may back multiple live jaws.Element values. Its HTML getter is shared by those Elements and must be safe for their render, update and event calls.

func NewSpan

func NewSpan(innerHTML any) *Span

NewSpan returns a span widget whose inner HTML is rendered from innerHTML.

innerHTML is passed to bind.MakeHTMLGetter; plain strings are trusted HTML.

func (*Span) JawsRender

func (u *Span) JawsRender(elem *jaws.Element, w io.Writer, params []any) error

JawsRender renders ui as an HTML span element.

type Tbody

type Tbody struct {
	Container
}

Tbody renders an HTML tbody containing dynamic child rows.

NewTbody configures its embedded Container for tbody. Replacing that Container is unsupported. Tbody otherwise follows Container's identity, reconciliation, multiplicity, and typed-nil-provider behavior. Treat it as immutable after use and use it as a value; taking its address changes identity and is unsupported.

func NewTbody

func NewTbody(children jaws.Container) Tbody

NewTbody returns a Tbody that renders children as table rows.

type Td

type Td struct{ HTMLInner }

Td renders an HTML table cell with dynamic inner HTML.

One Td value may back multiple live jaws.Element values. Its HTML getter is shared by those Elements and must be safe for their render, update and event calls.

func NewTd

func NewTd(innerHTML any) *Td

NewTd returns a table cell widget whose inner HTML is rendered from innerHTML.

innerHTML is passed to bind.MakeHTMLGetter; plain strings are trusted HTML.

func (*Td) JawsRender

func (u *Td) JawsRender(elem *jaws.Element, w io.Writer, params []any) error

JawsRender renders ui as an HTML table cell.

type Template

type Template struct {
	OuterHTMLTag string // Wrapper element; empty renders unwrapped and disables JawsUpdate.
	Name         string // Template name to be looked up using Jaws.LookupTemplate.
	Dot          any    // Template data, tag source, event delegate, and initial-attribute source.
}

Template renders a named Go html/template partial through JaWS.

Use Templates as values; NewTemplate is the usual constructor for wrapped Templates. A Template may back multiple live jaws.Element values in one jaws.Request; its Dot and callbacks must support all of their render, update, and event calls. Taking a Template's address is unsupported because it changes container reuse from value identity to pointer identity.

Dot may be a nil interface. Otherwise the Template must be comparable at runtime and equal to itself, and Dot must be usable as a tag under tag.TagExpand.

If Dot implements jaws.InitialHTMLAttrHandler, its callback supplies wrapper attributes separately for each wrapped Element's initial render. An unwrapped Template has no attribute target and does not invoke the callback. The callback is not invoked during Template.JawsUpdate.

OuterHTMLTag names the wrapper that receives the JaWS ID and render-time HTML attributes from render parameters and Dot. Render-parameter attributes take precedence when Dot returns an attribute with the same name. An empty field renders without a wrapper, making Template.JawsUpdate a no-op. NewTemplate defaults an empty wrapper argument to "div". The named template must be a partial; use Handler for a complete document.

A Template owns the Elements created through its RequestWriter. A successful update unregisters Elements from the previous execution. Elements created by a failed execution are also unregistered.

Replacing wrapper contents reports all managed descendants being removed in one WebSocket message, subject to jaws.Request.ServeHTTP's 32 KiB inbound limit. Split large trees into independently updated nested wrappers.

Execution is not transactional. An error may leave partial output, queued messages, or application side effects in place.

Example (FailureBehavior)
package main

import (
	"bytes"
	"fmt"
	"html/template"
	"strings"

	"github.com/linkdata/jaws"
	"github.com/linkdata/jaws/lib/tag"
	"github.com/linkdata/jaws/lib/ui"
)

func main() {
	tmpl := template.Must(template.New("partial").Parse(`before {{.Dot}} {{call .Missing}} after`))
	jw, err := jaws.New()
	if err != nil {
		panic(err)
	}
	defer jw.Close()
	if err = jw.AddTemplateLookuper(tmpl); err != nil {
		panic(err)
	}
	rq := jw.NewRequest(nil)
	elem := rq.NewElement(ui.NewTemplate("div", "partial", tag.Tag("dot")))

	var out bytes.Buffer
	err = elem.JawsRender(&out, nil)
	fmt.Println(strings.Contains(out.String(), `id="Jid.1"`))
	fmt.Println(strings.Contains(out.String(), "before dot"))
	fmt.Println(err != nil)

}
Output:
true
true
true

func NewTemplate

func NewTemplate(outerHTMLTag, name string, dot any) Template

NewTemplate returns a Template for rendering the named partial template with dot exposed as With.Dot.

outerHTMLTag names the generated wrapper element that owns the JaWS ID and render-time HTML attributes. If outerHTMLTag is empty, "div" is used. Choose a tag suitable for the DOM context. For an unwrapped fragment, use html/template's native {{template "name" pipeline}} action. The name is resolved at render and update time.

dot may be a nil interface. Otherwise it must make the returned Template comparable and equal to itself, and it must be usable as a tag under tag.TagExpand. Use the returned Template as a value; taking its address is unsupported. If dot implements jaws.InitialHTMLAttrHandler, its callback supplies attributes separately for each generated wrapper's initial render.

Example (DefaultWrapper)
package main

import (
	"fmt"

	"github.com/linkdata/jaws/lib/tag"
	"github.com/linkdata/jaws/lib/ui"
)

func main() {
	tmpl := ui.NewTemplate("", "partial", tag.Tag("dot"))
	fmt.Println(tmpl.OuterHTMLTag)

}
Output:
div

func (Template) JawsClick added in v0.401.0

func (tmpl Template) JawsClick(elem *jaws.Element, click jaws.Click) (err error)

JawsClick delegates click events to t.Dot when it implements jaws.ClickHandler.

func (Template) JawsContextMenu added in v0.401.0

func (tmpl Template) JawsContextMenu(elem *jaws.Element, click jaws.Click) (err error)

JawsContextMenu delegates context-menu events to t.Dot when it implements jaws.ContextMenuHandler.

func (Template) JawsInput added in v0.401.0

func (tmpl Template) JawsInput(elem *jaws.Element, value string) (err error)

JawsInput delegates input events to t.Dot when it implements jaws.InputHandler.

func (Template) JawsRender

func (tmpl Template) JawsRender(elem *jaws.Element, w io.Writer, params []any) (err error)

JawsRender renders t through the request's configured template lookupers.

If elem's widget state is occupied, JawsRender returns jaws.ErrElementStateClaimed without output. Other errors may leave partial output or side effects as described on Template.

func (Template) JawsUpdate

func (tmpl Template) JawsUpdate(elem *jaws.Element)

JawsUpdate re-renders the Template into its wrapper.

An empty OuterHTMLTag has no DOM target, so the update is a no-op. Otherwise elem must have been rendered by an equal Template value; using an unequal value is unsupported. After a successful lookup, missing Template state reports ErrElementStateUnclaimed.

On success, JawsUpdate replaces the wrapper content and unregisters Elements from the previous execution. On execution failure, it keeps the previous DOM and Elements and unregisters Elements created by the failed attempt.

Lookup, state, and execution errors are reported through jaws.Request.MustLog, which may panic when no jaws.Jaws.Logger is configured.

func (Template) String

func (tmpl Template) String() string

String returns a debug representation of t.

type Text

type Text struct{ InputText }

Text renders an HTML text input bound to a string setter.

A Text value must back at most one live jaws.Element. Construct distinct Text values over the same setter to render one bound value more than once.

func NewText

func NewText(g bind.Setter[string]) *Text

NewText returns a text input widget bound to g.

For writable use, g must provide the setter-derived dirty target described by Input.

func (*Text) JawsRender

func (u *Text) JawsRender(elem *jaws.Element, w io.Writer, params []any) error

JawsRender renders ui as an HTML text input.

type Textarea

type Textarea struct{ InputText }

Textarea renders an HTML textarea bound to a string setter.

A Textarea value must back at most one live jaws.Element. Construct distinct Textarea values over the same setter to render one bound value more than once.

While the WebSocket is open, each input event sends the complete value in one message. The value plus protocol and JSON overhead must fit the 32 KiB inbound limit documented by jaws.Request.ServeHTTP. Use a conservative maxlength or a separate upload endpoint for potentially large text.

func NewTextarea

func NewTextarea(g bind.Setter[string]) *Textarea

NewTextarea returns a textarea widget bound to g.

For writable use, g must provide the setter-derived dirty target described by Input. See Textarea for the browser-to-server message-size limit.

func (*Textarea) JawsRender

func (u *Textarea) JawsRender(elem *jaws.Element, w io.Writer, params []any) (err error)

JawsRender renders ui as an HTML textarea.

type Tr

type Tr struct{ HTMLInner }

Tr renders an HTML table row with dynamic inner HTML.

One Tr value may back multiple live jaws.Element values. Its HTML getter is shared by those Elements and must be safe for their render, update and event calls.

func NewTr

func NewTr(innerHTML any) *Tr

NewTr returns a table row widget whose inner HTML is rendered from innerHTML.

innerHTML is passed to bind.MakeHTMLGetter; plain strings are trusted HTML.

func (*Tr) JawsRender

func (u *Tr) JawsRender(elem *jaws.Element, w io.Writer, params []any) error

JawsRender renders ui as an HTML table row.

type With

type With struct {
	*jaws.Element     // the Element being rendered using a template
	RequestWriter     // the RequestWriter for nested UI helpers
	Dot           any // user data parameter
	// Auth is the authentication information from [jaws.Jaws.MakeAuth]. When
	// MakeAuth is nil it is a [jaws.DefaultAuth], whose IsAdmin returns true for
	// everyone — gating UI on {{if .Auth.IsAdmin}} is only safe once MakeAuth is
	// set. See [jaws.DefaultAuth] for the fail-open caveat.
	Auth jaws.Auth
}

With is passed as the data parameter when using RequestWriter.Template, populated with all required members set.

Jump to

Keyboard shortcuts

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