Skip to content

@jsvision/forms / bindField

Function: bindField()

bindField<T>(field, view): void

Defined in: bind-field.ts:56

Wire a field's touched flag to a widget's focus: the field becomes touched the first time focus leaves the widget (a blur), never merely by mounting it or focusing into it. This is how a form shows a validation error only after the user has visited and left a field, matching submit() (which marks every field touched at once).

Behavior and constraints:

  • First-leave only. Touched flips on the focused: true → false transition. It never fires on mount or on focus-in, and re-focusing then leaving again is harmless (touched stays true).
  • View-scoped lifetime. The focus effect is owned by the view and torn down when the view unmounts — no manual cleanup, no store-level subscription. Call it any time (before or after the view is mounted); the wiring is deferred to the view's mount.
  • Idempotent per (field, view). Calling it twice for the same pair wires the effect once.
  • Foreign handles throw. field must be a handle from this form's createForm; any other object throws FormFieldError (there is no touched seam registered for it).

Use it alongside a direct value binding (new Input({ value: field.value })) or a choice adapter (bindRadio/bindCheck) — those carry the value two-way; bindField adds only the touched signal.

Type Parameters

T

T

Parameters

field

Field<T>

The field handle whose touched flag to drive.

view

View

The focusable widget bound to that field.

Returns

void

Throws

If field was not produced by this form's createForm.

Example

ts
import { Group, Input, createEventLoop } from '@jsvision/ui';
import { resolveCapabilities } from '@jsvision/core';
import { createForm, bindField } from '@jsvision/forms';
import { z } from 'zod';

const form = createForm({ schema: z.object({ email: z.string().email() }), initial: { email: '' } });
const field = form.field('email');
const input = new Input({ value: field.value }); // two-way value binding
bindField(field, input);                          // touched once focus leaves the field

const root = new Group();
root.add(input);
const caps = resolveCapabilities({ env: {}, platform: 'linux' }).profile;
const loop = createEventLoop({ width: 30, height: 3 }, { caps });
loop.mount(root);
loop.focusView(input);
// field.touched() stays false while focused; it becomes true the first time focus moves away.