Docs

Form State

The value, the dirty and invalid states, and the errors of a form and its fields, and how to change the value of a field from code.

The result of useForm() describes the state of the form. The result of useFormPart() describes the state of a part of the form, such as a single field. The component re-renders when the state changes.

State Properties

The form and its parts have these properties:

value

The current value.

defaultValue

The value that was last read or submitted.

dirty

true when the value differs from the default value. A form stays dirty when the user changes a field back to its original value, because the form then has a new value object. A single field with a string, number, or boolean value compares the values themselves.

visited

true when the field has lost focus at least once. It’s only set for fields, not for the form or for parts that contain several fields.

required

true when the field is required, as described in Required Fields.

invalid

true when there are validation errors.

errors

The validation errors of the form or the part, including the errors of the fields that it contains. Each error has a property, which identifies the field by its model or its path, and a message. When a component reports its own input as invalid, for example because a required field is empty, the errors also contain an error with an empty message.

ownErrors

The validation errors that belong to the form or the part itself, and not to a field that it contains.

The form also has a submitting property, which is true while the onSubmit callback runs. See Submitting.

For example, the following button is enabled only when there is something to save, and the list shows all the errors of the form:

Source code
tsx
const { model, field, dirty, submitting, errors } = useForm(PersonModel, {
  onSubmit: async (person) => {
    await PersonService.savePerson(person);
  },
});

// ...

<Button onClick={save} disabled={!dirty || submitting}>
  Save
</Button>
<ul>
  {errors
    .filter((error) => error.message)
    .map((error, index) => (
      <li key={index}>{error.message}</li>
    ))}
</ul>

Changing Values from Code

To change the value of a field from code, call setValue() of its part, or of the form with a new value for the whole form. Don’t change the objects in value directly: the form doesn’t notice such changes, so it neither re-renders nor validates.

The following example has a combo box for a country and another for a city. When the country changes, the view loads the cities of the country, and selects the first city if the current one isn’t among them:

Source code
CompanyOfficeView.tsx

The form, the classes, and the service of the example are as follows:

Source code
CompanyOffice.java
package com.vaadin.demo.fusion.forms.formchange;

public class CompanyOffice {

    private Country country;
    private City city;

    public CompanyOffice() {
    }

    public CompanyOffice(Country country, City city) {
        this.country = country;
        this.city = city;
    }

    public Country getCountry() {
        return country;
    }

    public void setCountry(Country country) {
        this.country = country;
    }

    public City getCity() {
        return city;
    }

    public void setCity(City city) {
        this.city = city;
    }
}
CompanyOffice.java
Source code
OfficeService.java
package com.vaadin.demo.fusion.forms.formchange;

import com.vaadin.flow.server.auth.AnonymousAllowed;
import com.vaadin.hilla.BrowserCallable;

import java.util.List;

@BrowserCallable
@AnonymousAllowed
public class OfficeService {

    private final List<Country> countries = List.of(
            new Country("Germany",
                    List.of(new City("Berlin"), new City("Hamburg"),
                            new City("Munich"))),
            new Country("France",
                    List.of(new City("Paris"), new City("Marseille"),
                            new City("Lyon"))),
            new Country("Spain",
                    List.of(new City("Madrid"), new City("Barcelona"),
                            new City("Valencia"))),
            new Country("Italy",
                    List.of(new City("Rome"), new City("Milan"),
                            new City("Naples"))),
            new Country("United Kingdom", List.of(new City("London"),
                    new City("Birmingham"), new City("Glasgow"))));

    private CompanyOffice companyOffice = new CompanyOffice(countries.get(0),
            countries.get(0).getCities().get(0));

    public CompanyOffice loadCompanyOffice() {
        return companyOffice;
    }

    public void saveCompanyOffice(CompanyOffice companyOffice) {
        this.companyOffice = companyOffice;
    }

    public List<Country> loadCountries() {
        return countries;
    }

    public List<City> loadCities(String countryName) {
        // Find the cities by country
        return countries.stream()
                .filter(country -> country.getName().equals(countryName))
                .findFirst().map(Country::getCities).orElseThrow(
                        () -> new IllegalArgumentException("No such country"));
    }

}
OfficeService.java

Updated –