> Markdown version of [Form Binding](https://vaadin.com/docs/next/hilla/guides/forms/reference). Section index: [llms.txt](https://vaadin.com/docs/next/hilla/llms.txt)

# Form Binding Reference

The key concepts behind form binding are the `field()` directive, the `Model`, the `Binder` and binder nodes.

## <a id="the-field-directive"></a>The Field Directive

The `field()` directive does the main task of binding the form field components in the template by doing the following:

- sets the `name` attribute;

- implements two-way binding for the value state;

- sets the `required` (boolean) state; and

- sets the `invalid` (boolean) state and the `errorMessage` (string) when the current value is invalid.

Using the Field Directive

```tsx
import { TextField } from '@vaadin/react-components/TextField.js';
...
const { model, field } = useForm(PersonModel);
...
return (
  <TextField label="Full name" {...field(model.fullName)} />
);
```

Depending on the type of the bound component, the field directive selects a strategy that defines exactly how the states above are applied on the component, for example which attributes and properties of the element are used.

The field directive supports Vaadin components and HTML input elements. Vaadin components have support for all of the states. However, for HTML input elements, the `invalid`, `required` and `errorMessage` states aren’t displayed in the bound component. As a workaround, you can manually bind these in the template:

```tsx
import { useForm, useFormPart } from '@vaadin/hilla-react-form';
import { StringModel } from '@vaadin/hilla-lit-form';
...
interface FullNameProps {
    fullNameModel: StringModel;
}

function FullNameComponent({ fullNameModel }: FullNameProps) {

  const { model, field, required, errors, invalid } = useFormPart(fullNameModel);

  return (
    <>
      <label htmlFor="fullName">
        Full name
        {required ? '*' : ''}
      </label>
      <input id="fullName" {...field(model)} />
      <br/>
      <span className="label" style={{visibility: invalid ? 'visible' : 'hidden'}}>
        <strong>
          {errors[0]?.message}
        </strong>
      </span>
    </>
  );
}

export default function Main() {
  const { model } = useForm(PersonModel);
  return <FullNameComponent fullNameModel={model.fullName} />;
}
```

See the [Binding Data to Hilla Components](https://vaadin.com/docs/next/hilla/guides/forms/vaadin-components.md) for more, related information.

## <a id="the-form-model"></a>The Form Model

A form model describes the structure of the form. It allows individual fields to be referenced in a type-safe way, and is an alternative to strings such as `'person.firstName'`.

Typically, a model is used as an argument for the `field()` directive to specify the target form property to create a binding or to access the state. In contrast to string names such as `'person.firstName'`, typed form models allow autocompletion and static type checking, which makes creating forms faster and safer.

Hilla automatically generates Model classes for server-side services from Java beans. There’s usually no need to define models, manually.

Technically, every model instance represents either a key of a parent model, or the value of the `Binder` itself (e.g., the form data object).

> **Note:** The model classes aren’t intended to be instantiated manually. Instead, the `Binder` constructor receives the form model class and takes care of creating model instances.

### <a id="primitive-models"></a>Primitive Models

These are the built-in models that represent the common primitive field types:

| Type           | Value type `T` | Empty Value (`Model.createEmptyValue()` result) |
| -------------- | -------------- | ----------------------------------------------- |
| `StringModel`  | `string`       | `''`                                            |
| `NumberModel`  | `number`       | `0`                                             |
| `BooleanModel` | `boolean`      | `false`                                         |

Primitive models extend `PrimitiveModel<T>`. Primitive models are leaf nodes of the data structure; they don’t have nested field keys.

### <a id="object-models"></a>Object Models

The primitive editable values of the form are typically grouped into objects. This is accommodated through `ObjectModel<T>`, which is also a common superclass for all the models generated from Java beans.

The subclasses of `ObjectModel<T>` define the type argument constraints and the default type. In addition, the subclasses list all the public properties, following the shape of the described object.

For example, for the following Java bean:

`IdEntity.java`

```java
public class IdEntity {
    private String idString;

    public String getIdString() {
        return idString;
    }

    public void setIdString(String idString) {
        this.idString = idString;
    }
}
```

`Person.java`

```java
import jakarta.validation.constraints.NotEmpty;

public class Person extends IdEntity {
    @NotEmpty(message = "Cannot be empty")
    private String fullName;

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }
}
```

The following TypeScript interfaces are generated to type-check the services:

`IdEntity.ts`

```typescript
export default interface IdEntity {
  idString: string | undefined;
}
```

`Person.ts`

```typescript
import IdEntity from './IdEntity';

export default interface Person extends IdEntity {
  fullName: string | undefined;
}
```

The following models are generated for form binding:

`IdEntityModel.ts`

```typescript
import IdEntity from './IdEntity';

export default class IdEntityModel<T extends IdEntity = IdEntity> extends ObjectModel<T> {
  static createEmptyValue: () => IdEntity;
  readonly idString = new StringModel(this, 'idString');
}
```

`PersonModel.ts`

```typescript
import IdEntityModel from './IdEntityModel';

import Person from './Person';

export default class PersonModel<T extends Person = Person> extends IdEntityModel<T> {
  static createEmptyValue: () => Person;
  readonly fullName = new StringModel(this, 'fullName', new NotEmpty({message: 'Cannot be empty'}));
}
```

> **Caution:** To avoid naming collisions with user-defined object model fields, the built-in models and model superclasses don’t have any public instance properties or methods, aside from the `toString()` and `valueOf()` methods inherited from `AbstractModel<T>` (see following).

The properties of object models are intentionally read-only.

### <a id="array-model"></a>Array Model

`ArrayModel<T>` is used to represent array properties.

The type argument `T` in array models indicates the type of values in the array.

An array model instance contains the item model class reference. The item model is instantiated for every array entry, as necessary.

Array models are iterable. Iterating yields binder nodes for entries:

```tsx
import { TextField } from '@vaadin/react-components/TextField.js';

interface PersonProps {
  model: PersonModel;
}

function Person({ model }: PersonProps) {
  const { field, model, value } = useFormPart(model);

  return (
    <div>
      <TextField label="Full name" {...field(model.fullName)} />
      <strong>Full name:</strong>
      {value.fullName}
    </div>
  );
}
...
return (
  <>
    {model.people.map(person => <Person model={person} />)}
  </>
);
```

The array entries aren’t available for indexing with bracket notation (`[]`).

### <a id="abstract-model-superclass"></a>Abstract Model Superclass

All models subclass from the `AbstractModel<T>` TypeScript class, where the `T` type argument refers to the value type.

#### <a id="empty-value-definition"></a>Empty Value Definition

Model classes define an empty value, which is used to initialize the `defaultValue` and `value` properties, and also for `clear()`.

For this purpose, `AbstractModel<T>`, as well as every subclass, has a method `static createEmptyValue(): T`, which returns the empty value of the subject model type.

```typescript
const emptyPerson: Person = PersonModel.createEmptyValue();
console.log(emptyPerson); // {"fullName": ""}
```

#### <a id="models-in-expressions"></a>Models in Expressions

As with any JavaScript object, `AbstractModel<T>` has `toString(): string` and `valueOf(): T` instance methods, but, as we know that the model is just metadata, they cannot return any values. Then, those instance methods must be called on the `value` property obtained from calling `useForm` instead:

```tsx
const { model, value } = useForm(PersonModel);
...
return (
  <>
    {value.name.toString()}
    {value.name.valueOf()}
    {value.name}
  </>
)
```

Then, it’s possible to use the values in formulas using either of the following:

```tsx
return (
  <>
    Cost: {value.quantity.valueOf() * value.price.valueOf()}
    Cost: {value.quantity * value.price}
  </>
);
```

## <a id="binder"></a>The Binder

A form binder controls all aspects of a single form. It’s typically used to get and set the form value, access the form model, validate, reset, and submit the form.

The `Binder` constructor arguments are:

- `Model: ModelConstructor<T, M>`

  The constructor (class reference) of the form model. The `Binder` instantiates the top-level model; and

- `config?: BinderConfiguration<T>`

  The options object.

  - `onChange?: (oldValue?: T) ⇒ void`

    The callback that updates the form view;

  - `onSubmit?: (value: T) ⇒ Promise<T | void>`

    The service method to submit the form data to.

The `Binder` has the following instance properties:

- `model: M`

  The form model, the top-level model instance created by the `Binder`.

- `value: T`

  The current value of the form, two-way bound to the field components.

- `defaultValue: T`

  The initial value of the form, before any fields are edited by the user.

- `readonly validating: boolean`

  True when there is an ongoing validation.

- `readonly submitting: boolean`

  True if the form was submitted, but the submit promise isn’t resolved yet.

The `Binder` instance methods are:

- `read(value: T): void`

  Load the given value to the form.

- `reset(): void`

  Reset the form to the previous value.

- `clear(): void`

  Sets the form to empty value, as defined in the Model.

- `getFieldStrategy(element: any): FieldStrategy`

  Determines and returns the `field` directive strategy for the bound element. Override to customize the binding strategy for a component. The `Binder` extends `BinderNode`; see the inherited properties and methods that follow.

## <a id="binder-node"></a>Binder Nodes

The `BinderNode<T, M>` class provides the form-binding-related APIs with respect to a particular model instance.

Structurally, model instances form a tree in which the object and array models have child nodes of field and array item model instances.

Every model instance has a one-to-one mapping to a corresponding `BinderNode` instance. The `Binder` itself is a `BinderNode` for the top-level form model. The binder nodes have the following properties:

- `model: M`

  The model instance mapped to this binder node.

- `value: T`

  The current value related to the model, two-way bound to the field components.

- `readonly defaultValue: T`

  The default value related to the model. Note: this is read-only here; use the top-level `binder.defaultValue` to change.

- `parent: BinderNode<any, AbstractModel<any>> | undefined`

  The parent node, if this binder node corresponds to a nested model; otherwise, `undefined` for the top-level binder.

- `binder: Binder<any, AbstractModel<any>>`

  The binder for the top-level model.

- `readonly name: string`

  The name generated from the model structure, used to set the `name` attribute on the field components.

- `readonly required: boolean`

  True if the value is required to be non-empty. Based on the presence of validators that have the `impliesRequired: true` flag.

- `dirty: boolean`

  True if the current `value` is different from the `defaultValue`.

- `visited: boolean`

  True if the bound field was ever focused and blurred by the user. The value is set by the `field` directive.

- `validators: ReadonlyArray<Validator<T>>`

  The array of validators for the model. The default value is defined in the model.

- `readonly ownErrors: ReadonlyArray<ValueError<T>>`

  The array of validation errors directly related with the model.

- `readonly errors: ReadonlyArray<ValueError<any>>`

  The combined array of all errors for this node’s model and all its nested models.

- `readonly invalid: boolean`

  True when the `errors` array isn’t empty.

The binder node has the following instance methods:

- `for<NM extends AbstractModel<any>>(model: NM): BinderNode<ModelType<NM>, NM>`

  Returns a binder node for the nested model instance.

- `async validate(): Promise<ReadonlyArray<ValueError<any>>>`

  Runs all validation callbacks potentially affecting this or any nested model. Returns the combined array of all errors, as in the `errors` property.

- `addValidator(validator: Validator<T>): void`

  A helper method to add a validator to the `validators`.

- `appendItem(itemValue?: T): void`

  A helper method for array models. If the node’s model is an `ArrayModel<T>`, appends an item to the array; otherwise throws an exception. If the argument is given, the argument value is used for the new item; otherwise, a new empty item is created.

- `prependItem(itemValue?: T): void`

  A helper method for array modes, similar to `appendItem()`, but prepends an item to the array.

- `removeSelf(): void`

  A helper method for array item models. If the node’s **parent model** is an `ArrayModel<T>`, removes the item the array; otherwise throws an exception.
