Documentation
¶
Overview ¶
Package bind adapts Go values to JaWS getter, setter, HTML, tag, and event interfaces.
New creates the usual binding from a locker-protected pointer. The pointer remains the binding's tag through every Binder builder, and each builder returns a new chain rather than mutating the earlier value. Widgets bound to the same pointer therefore share one dirty identity.
MakeHTMLGetter defines the package's HTML conversion boundary. Existing HTMLGetter values are used unchanged; plain strings and html/template.HTML are trusted. Its adapters for string-valued Getter and Binder values and fmt.Stringer output escape their strings. Escape untrusted text before it reaches a trusted form.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrValueNotSettable = errors.New("value not settable")
ErrValueNotSettable is returned by read-only adapters when Setter.JawsSet is called.
Functions ¶
This section is empty.
Types ¶
type Binder ¶
type Binder[T comparable] interface { RWLocker Setter[T] tag.TagGetter jaws.ClickHandler jaws.ContextMenuHandler jaws.InitialHTMLAttrHandler // JawsGetLocked returns the bound value while the Binder lock is held. // // Callers must already hold the lock, preferring the read lock if available; // the method does not lock or unlock. It is the value path: it applies this // chain's [GetHook]s and never invokes HTML rendering ([GetHTMLHook]), so // calling it from within a GetHTMLHook to read the value does not recurse. JawsGetLocked(elem *jaws.Element) (value T) // JawsSetLocked stores value while the Binder write lock is held. // // Callers must already hold the write lock; the method does not lock or // unlock and must not be called (nor [Setter.JawsSet] called) from within a // hook. It applies this chain's [SetHook]s. // // The [Binder] returned by [New] stores value when it differs from the stored // value and returns [jaws.ErrValueUnchanged] otherwise. This comparison may // panic unless T is strictly comparable. JawsSetLocked(elem *jaws.Element, value T) (err error) // JawsInitialHTMLAttrLocked returns the initial HTML attribute while the // Binder lock is held. // // Callers must already hold the lock, preferring the read lock if available; // the method does not lock or unlock. It applies this chain's // [InitialHTMLAttrHook]s and returns an empty [html/template.HTMLAttr] when no // such hook is present anywhere in the chain. JawsInitialHTMLAttrLocked(elem *jaws.Element) (s template.HTMLAttr) // SetLocked returns a [Binder] that will call fn instead of [Binder.JawsSetLocked]. // // The lock will be held at this point. // Do not lock or unlock the [Binder] within fn. Do not call [Setter.JawsSet]. // // The bind argument to the function is the previous Binder in the chain, // and you probably want to call its [Binder.JawsSetLocked] first. SetLocked(fn SetHook[T]) (newbind Binder[T]) // GetLocked returns a [Binder] that will call fn instead of [Binder.JawsGetLocked]. // // The lock will be held at this point, preferring RLock over Lock, if available. // Do not lock or unlock the [Binder] within fn. Do not call [Getter.JawsGet]. // // The bind argument to the function is the previous Binder in the chain, // and you probably want to call its [Binder.JawsGetLocked] first. GetLocked(fn GetHook[T]) (newbind Binder[T]) // Success returns a [Binder] that calls fn after a successful set. // // No locks are held when the function is called. // If the function returns an error, that will be returned from [Setter.JawsSet]. // // The dynamic type of fn must be one of the following function types: // * func() // * func() error // * func(*jaws.Element) // * func(*jaws.Element) error // // [SuccessHook] is an alias for the last function type. // A value of a defined function type must be converted to the corresponding // function type before it is passed. Success panics if fn has any other // dynamic type. Success(fn any) (newbind Binder[T]) // GetHTML returns a [Binder] that will call fn instead of the default // escaped [fmt.Sprint] HTML rendering. // // The lock will be held at this point, preferring RLock over Lock, if available. // Do not lock or unlock the [Binder] within fn. Do not call [Getter.JawsGet]. // // Unlike [Binder.GetLocked] and [Binder.SetLocked], the bind argument to fn is // the current Binder, not the previous one; read the value with its // JawsGetLocked to render it. See [GetHTMLHook]. // // GetHTML and [Binder.Format] are both HTML-rendering overrides resolved // head-first, so when a chain has more than one the most recently added wins // and shadows any earlier one. GetHTML(fn GetHTMLHook[T]) (newbind Binder[T]) // Format returns a [Binder] that formats its bound value. // // With the Binder lock held, it calls [Formatter.Format] when T implements // [Formatter], or [fmt.Sprintf] otherwise, then escapes the result with // [html.EscapeString]. // // When chained with [Binder.GetHTML], the most recently added rendering // override wins. Format(format string) (newbind Binder[T]) // Clicked returns a [Binder] that will call fn when [jaws.ClickHandler.JawsClick] is invoked. // // The [Binder] locks are not held when the function is called. Clicked(fn ClickedHook[T]) (newbind Binder[T]) // ContextMenu returns a [Binder] that will call fn when // [jaws.ContextMenuHandler.JawsContextMenu] is invoked. // // The [Binder] locks are not held when the function is called. ContextMenu(fn ContextMenuHook[T]) (newbind Binder[T]) // InitialHTMLAttr returns a [Binder] that will call fn when // [jaws.InitialHTMLAttrHandler.JawsInitialHTMLAttr] is invoked. // // The lock will be held at this point, preferring RLock over Lock, if available. // Do not lock or unlock the [Binder] within fn. Do not call [Getter.JawsGet]. // To call the previous handler in the chain, call [Binder.JawsInitialHTMLAttrLocked]. InitialHTMLAttr(fn InitialHTMLAttrHook[T]) (newbind Binder[T]) }
Binder binds a Go value to JaWS getter, setter, tag and event interfaces.
T must be strictly comparable. Interface types, including any, are unsupported; an array's element type and every struct field type must also be strictly comparable, regardless of the bound values. The default Binder.JawsSetLocked comparison may panic when T satisfies the comparable constraint but is not strictly comparable.
Binder methods are safe for concurrent use when the locker passed to New is safe for concurrent use.
Binder holds its lock while rendering HTML, including while invoking GetHTMLHook, Formatter.Format, fmt.Formatter.Format and fmt.Stringer.String, and while invoking InitialHTMLAttrHook.
Example (Hooks) ¶
package main
import (
"fmt"
"html/template"
"sync"
"github.com/linkdata/jaws"
"github.com/linkdata/jaws/lib/bind"
)
func main() {
var mu sync.Mutex
value := 1
var calls []string
b := bind.New(&mu, &value).
SetLocked(func(prev bind.Binder[int], elem *jaws.Element, value int) error {
calls = append(calls, "set-locked")
return prev.JawsSetLocked(elem, value)
}).
GetHTML(func(cur bind.Binder[int], elem *jaws.Element) template.HTML {
calls = append(calls, "get-html")
return template.HTML(fmt.Sprintf("<strong>%d</strong>", cur.JawsGetLocked(elem))) // #nosec G203
}).
Success(func() {
calls = append(calls, "success-oldest")
}).
Success(func() {
calls = append(calls, "success-newest")
})
if err := b.JawsSet(nil, 2); err != nil {
panic(err)
}
htmlGetter := b.(bind.HTMLGetter)
fmt.Println(value)
fmt.Println(htmlGetter.JawsGetHTML(nil))
fmt.Println(calls)
}
Output: 2 <strong>2</strong> [set-locked success-newest success-oldest get-html]
func New ¶
func New[T comparable](l sync.Locker, p *T) Binder[T]
New returns a Binder with l protecting the value pointed to by p.
T must be strictly comparable. Interface types, including any, are unsupported; an array's element type and every struct field type must also be strictly comparable, regardless of the bound values. The default Binder.JawsSetLocked comparison may panic when T satisfies the comparable constraint but is not strictly comparable.
If l implements RWLocker, reads use its read lock. Otherwise reads and writes both use l. The pointer p is also exposed as the UI tag.
type ClickedHook ¶ added in v0.400.0
type ClickedHook[T comparable] func(bind Binder[T], elem *jaws.Element, click jaws.Click) (err error)
ClickedHook is a function to call when a click event is received.
The Binder locks are not held when the function is called.
Like GetHTMLHook and unlike GetHook and SetHook, the bind argument is the current Binder (the one whose hook is being invoked), not the previous one.
type ContextMenuHook ¶ added in v0.400.0
type ContextMenuHook[T comparable] func(bind Binder[T], elem *jaws.Element, click jaws.Click) (err error)
ContextMenuHook is a function to call when a context menu event is received.
The Binder locks are not held when the function is called.
Like GetHTMLHook and unlike GetHook and SetHook, the bind argument is the current Binder (the one whose hook is being invoked), not the previous one.
type Formatter ¶
Formatter customizes Binder.Format output for a value.
type GetHTMLHook ¶ added in v0.400.0
GetHTMLHook is a function to call when HTMLGetter.JawsGetHTML is called.
The lock will be held before calling the function, preferring RLock over Lock, if available. Do not lock or unlock the Binder in the function. Do not call Getter.JawsGet or HTMLGetter.JawsGetHTML (either would deadlock or recurse).
Unlike GetHook and SetHook, the bind argument is the current Binder (the one whose hook is being invoked), not the previous one. Read the bound value with bind.JawsGetLocked(elem) to render it; that skips this hook and so does not recurse.
type GetHook ¶ added in v0.400.0
type GetHook[T comparable] func(bind Binder[T], elem *jaws.Element) (value T)
GetHook is a function that replaces Binder.JawsGetLocked.
The lock will be held before calling the function, preferring RLock over Lock, if available. Do not lock or unlock the Binder in the function. Do not call Getter.JawsGet.
The bind argument is the previous Binder in the chain, and you probably want to call its Binder.JawsGetLocked first.
type Getter ¶
type Getter[T comparable] interface { JawsGet(elem *jaws.Element) (value T) }
Getter exposes a value for a jaws.Element.
func MakeGetter ¶
func MakeGetter[T comparable](value any) Getter[T]
MakeGetter returns value as a Getter.
value may be a Getter of the same type or a static value of type T. It panics for any other type. An existing Getter is returned unchanged. A static value becomes an untagged Getter that does not satisfy Setter.
func StringGetterFunc ¶
StringGetterFunc wraps fn as a Getter for string values.
Optional tags are exposed through github.com/linkdata/jaws/lib/tag.TagGetter.
The top-level slots of tags are copied, so the caller may reuse or modify the slice it passed. Nested containers and reference-backed tag values are not copied; keeping those stable remains the caller's obligation.
type HTMLGetter ¶
HTMLGetter is the primary way to deliver generated HTML content to dynamic HTML nodes.
func HTMLGetterFunc ¶
HTMLGetterFunc wraps fn as an HTMLGetter.
Optional tags are exposed through tag.TagGetter.
The top-level slots of tags are copied, so the caller may reuse or modify the slice it passed. Nested containers and reference-backed tag values are not copied; keeping those stable remains the caller's obligation.
func MakeHTMLGetter ¶
func MakeHTMLGetter(value any) HTMLGetter
MakeHTMLGetter returns an HTMLGetter for value.
The first matching conversion is used:
- HTMLGetter is returned unchanged.
- template.HTML is used unchanged.
- Binder[string] and Getter[string] use escaped Getter.JawsGet output.
- fmt.Stringer uses escaped fmt.Stringer.String output.
- string is used unchanged.
- Other values use escaped fmt.Sprint output.
Getter[string] and fmt.Stringer adapters expose the wrapped value as an implicit tag. The value must be accepted by tag.TagExpand, directly or through tag.TagGetter; JawsGetTag may return nil to leave it untagged.
Plain strings are not escaped; do not pass untrusted text as a plain string.
type InitialHTMLAttrHook ¶ added in v0.400.0
type InitialHTMLAttrHook[T comparable] func(bind Binder[T], elem *jaws.Element) (s template.HTMLAttr)
InitialHTMLAttrHook is a function to call when an Element is initially rendered.
The lock will be held at this point, preferring RLock over Lock, if available. Do not lock or unlock the Binder within fn. Do not call Getter.JawsGet.
type RWLocker ¶
RWLocker is the subset of sync.RWMutex used by binders.
type SetHook ¶ added in v0.400.0
type SetHook[T comparable] func(bind Binder[T], elem *jaws.Element, value T) (err error)
SetHook is a function that replaces Binder.JawsSetLocked.
The Binder write lock will be held before calling the function. Do not lock or unlock the Binder in the function. Do not call [Binder.JawsSet].
The bind argument is the previous Binder in the chain, and you probably want to call its Binder.JawsSetLocked first.
type Setter ¶
type Setter[T comparable] interface { Getter[T] // JawsSet may return [jaws.ErrValueUnchanged] to indicate value was already set. JawsSet(elem *jaws.Element, value T) (err error) }
Setter exposes and updates a value for a jaws.Element.
func MakeSetter ¶
func MakeSetter[T comparable](value any) Setter[T]
MakeSetter returns value as a Setter.
value may be a Setter of the same type, a Getter of the same type or a static value of type T. Getter and static adapters are read-only and return ErrValueNotSettable from Setter.JawsSet. MakeSetter panics for any other type.
The adapters still satisfy Setter, so github.com/linkdata/jaws/lib/ui.Number and github.com/linkdata/jaws/lib/ui.Range apply their editable-source rules. Pass an existing Getter directly, or use MakeGetter for a static value, to render a read-only numeric control.
type SuccessHook ¶ added in v0.400.0
SuccessHook is called by Setter.JawsSet after Binder.JawsSetLocked succeeds.
The Binder locks are not held when the function is called.
Success hooks in a Binder chain are called in reverse registration order. If one of them returns an error, that error is returned from Setter.JawsSet and no more success hooks are called.
SuccessHook is a type alias so its values have the dynamic function type accepted by Binder.Success through its any parameter.