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.