# Controlled fields

> This document is the Markdown version of [formisch.dev/angular/guides/controlled-fields/](https://formisch.dev/angular/guides/controlled-fields/). For the complete documentation index, see [llms.txt](https://formisch.dev/llms.txt).

In Formisch for Angular, every field bound with the [`[formischControl]`](/angular/api/formischControl.md) directive is controlled for you. This is different from our other framework packages, where you have to bind the value of the element yourself.

## How it works

The directive writes the field's input into the element after every render in which the input changed. It knows the quirks of each element type, so it sets `value` on a text input, `checked` on a checkbox or radio, and the selected state of the options of a `<select />`.

```angular-html
<input
  *formischField="['firstName'] of form; let field"
  [formischControl]="field"
  type="text"
/>
```

That single binding is all you need for initial values, for programmatic updates via [`setInput`](/methods/api/setInput.md), and for [`reset`](/methods/api/reset.md) to be reflected in the UI. There is no `[value]`, `[checked]` or `[(ngModel)]` to add — and you must not add them, because the directive already owns the element's value.

> Writing happens in Angular's render write phase, after sibling DOM such as the options of a `<select />` has been created. That ordering is what makes a select show the right option on first render.

The directive additionally keeps the element's `name` attribute and its `aria-invalid` attribute in sync, and registers the element with the field so that [`focus`](/methods/api/focus.md) and the automatic focus of the first invalid field on submit work.

## Reading the value

To render the current value somewhere else — a character counter, a preview, a summary — read the `input` signal of the field:

```angular-html
<ng-container *formischField="['bio'] of form; let field">
  <textarea [formischControl]="field"></textarea>
  <span>{{ field.input()?.length ?? 0 }} / 500</span>
</ng-container>
```

Because it is a signal, only the parts of your template that actually read it update when the value changes.

## Setting the value

To change a field's value from your component class, you have two options:

- `field.setInput(value)` on the field store when you already have the field
- The [`setInput`](/methods/api/setInput.md) method when you only
  have the form store

```ts
readonly form = injectForm({ schema: ProfileSchema });

resetUsername(): void {
  setInput(this.form, { path: ['username'], input: '' });
}
```

Both update the field input and validate it like an `input` event would, so whether validation runs is controlled by your `validate` and `revalidate` config. The bound element follows automatically.

## Fields without a native element

If you use a UI library whose components do not expose their underlying native element, you cannot apply the [`[formischControl]`](/angular/api/formischControl.md) directive. In that case, control the field yourself by reading `field.input()` and writing back with `field.setInput(…)`:

```angular-html
<ng-container *formischField="['country'] of form; let field">
  <ui-select [value]="field.input()" (valueChange)="field.setInput($event)" />
  @if (field.errors(); as errors) {
    <div>{{ errors[0] }}</div>
  }
</ng-container>
```

Note that a field controlled this way is not registered as an element of the form. Features that need a DOM element — focusing the first invalid field on submit, and the [`focus`](/methods/api/focus.md) method — will skip it.

## Numbers and dates

An `<input type="number" />` and an `<input type="date" />` both expose their value as a string, and that string is what Formisch stores. Let your schema do the conversion so the validated output has the type you want:

```ts
const ProfileSchema = v.object({
  // Input: string from the number input, output: number
  age: v.pipe(
    v.string(),
    v.nonEmpty('Please enter your age.'),
    v.transform(Number),
    v.minValue(18)
  ),
  // Input and output: 'yyyy-mm-dd' string from the date input
  birthday: v.pipe(v.string(), v.nonEmpty('Please enter your birthday.')),
});
```

This keeps the field input a plain string that the element can hold, while [your submit handler](/angular/guides/handle-submission.md) receives the transformed value.

## File inputs

The HTML `<input type="file" />` element is an exception, because browsers do not allow a file selection to be set programmatically. Formisch can only clear it. You can, however, control the UI around it. For inspiration, check out our [file input](https://github.com/open-circle/formisch/blob/main/playgrounds/angular/src/components/file-input.component.ts) component from the [playground](/playground/login/).

## Next steps

Now that you understand controlled fields, you can explore more advanced topics like [nested fields](/angular/guides/nested-fields.md) and [field arrays](/angular/guides/field-arrays.md) to handle complex form structures.
