# TerminalFieldController

Source: /tuil/docs/reference/packages/form/api/terminal-field-controller
Locale: en

class exported by @mwillbanks/tuil-form.



{/* Generated by tooling/docs/generate-reference.ts. */}

## class [#class]

Public class exported by `@mwillbanks/tuil-form`.

```ts
export class TerminalFieldController<T> {
  #options: TerminalFieldOptions<T>;
  #state: TerminalFieldState<T>;
  readonly #observers = new Set<FieldObserver>();
  #validation?: AbortController;
  #validationSequence = 0;

  constructor(options: TerminalFieldOptions<T>) {
    this.#options = options;
    const value = options.value ?? options.initialValue;
    this.#state = Object.freeze({
      name: options.name,
      value,
      initialValue: options.initialValue,
      errors: [],
      touched: false,
      dirty: !Object.is(value, options.initialValue),
      validating: false,
      disabled: options.disabled ?? false,
      readOnly: options.readOnly ?? false,
      valid: true,
    });
  }

  get state(): TerminalFieldState<T> {
    return this.#state;
  }

  get secret(): boolean {
    return this.#options.secret ?? false;
  }

  subscribe(observer: FieldObserver): () => void {
    this.#observers.add(observer);
    return () => this.#observers.delete(observer);
  }

  configure(options: TerminalFieldOptions<T>): void {
    this.#options = options;
    const externalValue = options.value;
    const nextValue =
      externalValue === undefined ? this.#state.value : externalValue;
    const next = {
      ...this.#state,
      name: options.name,
      value: nextValue,
      initialValue: options.initialValue,
      dirty: !Object.is(nextValue, options.initialValue),
      disabled: options.disabled ?? false,
      readOnly: options.readOnly ?? false,
    };
    if (
      next.name !== this.#state.name ||
      !Object.is(next.value, this.#state.value) ||
      !Object.is(next.initialValue, this.#state.initialValue) ||
      next.dirty !== this.#state.dirty ||
      next.disabled !== this.#state.disabled ||
      next.readOnly !== this.#state.readOnly
    ) {
      this.#setState(next);
    }
  }

  async setValue(
    value: T,
    options: {
      readonly validate?: boolean;
      readonly signal?: AbortSignal;
    } = {},
  ): Promise<TerminalFieldState<T>> {
    if (this.#state.disabled || this.#state.readOnly) return this.#state;
    this.#setState({
      ...this.#state,
      value,
      dirty: !Object.is(value, this.#state.initialValue),
    });
    await this.#options.onValueChange?.(value);
    return options.validate === false
      ? this.#state
      : this.validate("change", options.signal);
  }

  async blur(signal?: AbortSignal): Promise<TerminalFieldState<T>> {
    this.#setState({ ...this.#state, touched: true });
    return this.validate("blur", signal);
  }

  async validate(
    trigger: ValidationTrigger = "command",
    signal?: AbortSignal,
  ): Promise<TerminalFieldState<T>> {
    const validators = validatorsFor(this.#options.validators, trigger);
    this.#validation?.abort();
    const validation = new AbortController();
    this.#validation = validation;
    const validationSignal = combinedSignal(validation.signal, signal);
    const sequence = ++this.#validationSequence;
    if (trigger === "submit") {
      this.#setState({ ...this.#state, touched: true, validating: true });
    } else {
      this.#setState({ ...this.#state, validating: true });
    }
    const errors: string[] = [];
    try {
      for (const validator of validators) {
        validationSignal.throwIfAborted();
        const error = await validator(this.#state.value, {
          signal: validationSignal,
          trigger,
        });
        validationSignal.throwIfAborted();
        if (error) errors.push(error);
      }
    } catch (error) {
      if (sequence === this.#validationSequence) {
        this.#setState({ ...this.#state, validating: false });
      }
      if (validationSignal.aborted) return this.#state;
      throw error;
    }
    if (sequence !== this.#validationSequence) return this.#state;
    this.#setState({
      ...this.#state,
      errors: Object.freeze(errors),
      validating: false,
      valid: errors.length === 0,
    });
    return this.#state;
  }

  setErrors(errors: readonly string[]): void {
    this.#setState({
      ...this.#state,
      errors: Object.freeze([...errors]),
      valid: errors.length === 0,
      validating: false,
    });
  }

  reset(value: T = this.#state.initialValue): void {
    this.#validation?.abort();
    this.#validationSequence += 1;
    this.#setState({
      ...this.#state,
      value,
      initialValue: value,
      errors: [],
      touched: false,
      dirty: false,
      validating: false,
      valid: true,
    });
  }

  serialize(redactSecrets = true): T | "[REDACTED]" {
    return redactSecrets && this.secret ? "[REDACTED]" : this.#state.value;
  }

  dispose(): void {
    this.#validation?.abort();
    this.#observers.clear();
  }

  #setState(state: TerminalFieldState<T>): void {
    this.#state = Object.freeze(state);
    for (const observer of this.#observers) observer();
  }
}
```

## Members [#members]

| Member                | Type                                                                                                                                | Required | Description                                                                                                                                                                  | Related types                                                                                                                                               |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `#options`            | `TerminalFieldOptions<T>`                                                                                                           | Yes      | The `#options` member uses the `TerminalFieldOptions<T>` contract.                                                                                                           | [`TerminalFieldOptions`](/tuil/docs/reference/packages/form/api/terminal-field-options)                                                                          |
| `#state`              | `TerminalFieldState<T>`                                                                                                             | Yes      | The `#state` member uses the `TerminalFieldState<T>` contract.                                                                                                               | [`TerminalFieldState`](/tuil/docs/reference/packages/form/api/terminal-field-state)                                                                              |
| `#observers`          | `Set<FieldObserver>`                                                                                                                | Yes      | The `#observers` member uses the `Set<FieldObserver>` contract.                                                                                                              | —                                                                                                                                                           |
| `#validation`         | `AbortController \| undefined`                                                                                                      | No       | The `#validation` member uses the `AbortController \| undefined` contract.                                                                                                   | —                                                                                                                                                           |
| `#validationSequence` | `number`                                                                                                                            | Yes      | The `#validationSequence` member uses the `number` contract.                                                                                                                 | —                                                                                                                                                           |
| `__constructor`       | `any`                                                                                                                               | Yes      | The `__constructor` member uses the `any` contract.                                                                                                                          | —                                                                                                                                                           |
| `state`               | `TerminalFieldState<T>`                                                                                                             | Yes      | The `state` member uses the `TerminalFieldState<T>` contract.                                                                                                                | [`TerminalFieldState`](/tuil/docs/reference/packages/form/api/terminal-field-state)                                                                              |
| `secret`              | `boolean`                                                                                                                           | Yes      | The `secret` member uses the `boolean` contract.                                                                                                                             | —                                                                                                                                                           |
| `subscribe`           | `(observer: FieldObserver) => () => void`                                                                                           | Yes      | The `subscribe` member uses the `(observer: FieldObserver) => () => void` contract.                                                                                          | —                                                                                                                                                           |
| `configure`           | `(options: TerminalFieldOptions<T>) => void`                                                                                        | Yes      | The `configure` member uses the `(options: TerminalFieldOptions<T>) => void` contract.                                                                                       | [`TerminalFieldOptions`](/tuil/docs/reference/packages/form/api/terminal-field-options)                                                                          |
| `setValue`            | `(value: T, options?: &#123; readonly validate?: boolean; readonly signal?: AbortSignal; &#125;) => Promise<TerminalFieldState<T>>` | Yes      | The `setValue` member uses the `(value: T, options?: &#123; readonly validate?: boolean; readonly signal?: AbortSignal; &#125;) => Promise<TerminalFieldState<T>>` contract. | [`TerminalFieldState`](/tuil/docs/reference/packages/form/api/terminal-field-state)                                                                              |
| `blur`                | `(signal?: AbortSignal) => Promise<TerminalFieldState<T>>`                                                                          | Yes      | The `blur` member uses the `(signal?: AbortSignal) => Promise<TerminalFieldState<T>>` contract.                                                                              | [`TerminalFieldState`](/tuil/docs/reference/packages/form/api/terminal-field-state)                                                                              |
| `validate`            | `(trigger?: ValidationTrigger, signal?: AbortSignal) => Promise<TerminalFieldState<T>>`                                             | Yes      | The `validate` member uses the `(trigger?: ValidationTrigger, signal?: AbortSignal) => Promise<TerminalFieldState<T>>` contract.                                             | [`TerminalFieldState`](/tuil/docs/reference/packages/form/api/terminal-field-state), [`ValidationTrigger`](/tuil/docs/reference/packages/form/api/validation-trigger) |
| `setErrors`           | `(errors: readonly string[]) => void`                                                                                               | Yes      | The `setErrors` member uses the `(errors: readonly string[]) => void` contract.                                                                                              | —                                                                                                                                                           |
| `reset`               | `(value?: T) => void`                                                                                                               | Yes      | The `reset` member uses the `(value?: T) => void` contract.                                                                                                                  | —                                                                                                                                                           |
| `serialize`           | `(redactSecrets?: boolean) => T \| "[REDACTED]"`                                                                                    | Yes      | The `serialize` member uses the `(redactSecrets?: boolean) => T \| "[REDACTED]"` contract.                                                                                   | —                                                                                                                                                           |
| `dispose`             | `() => void`                                                                                                                        | Yes      | The `dispose` member uses the `() => void` contract.                                                                                                                         | —                                                                                                                                                           |
| `#setState`           | `(state: TerminalFieldState<T>) => void`                                                                                            | Yes      | The `#setState` member uses the `(state: TerminalFieldState<T>) => void` contract.                                                                                           | [`TerminalFieldState`](/tuil/docs/reference/packages/form/api/terminal-field-state)                                                                              |

## Parameters [#parameters]

This declaration has no public members.

## Returns [#returns]

This declaration does not return a value.

## Throws [#throws]

No thrown errors are documented for this declaration.

## Related types [#related-types]

* [`TerminalFieldOptions`](/tuil/docs/reference/packages/form/api/terminal-field-options)
* [`TerminalFieldState`](/tuil/docs/reference/packages/form/api/terminal-field-state)
* [`ValidationTrigger`](/tuil/docs/reference/packages/form/api/validation-trigger)

## Source [#source]

[View the secondary source reference](https://github.com/mwillbanks/tuil/blob/main/packages/form/src/index.tsx)

## Package [#package]

[@mwillbanks/tuil-form](/tuil/docs/reference/packages/form)
