ComponentUpdated in v15

Form Field

Wraps a form control with a label, a hint and its error messages.

Installation

Import the class, and @use the entry point in your global styles: without it the component renders unstyled.

import { Component } from '@angular/core';
import { WrButton } from 'ngwr/button';
import { WrFormError, WrFormField } from 'ngwr/form';
import { WrInput } from 'ngwr/input';

@Component({ imports: [WrButton, WrFormError, WrFormField, WrInput] })
export class MyComponent {}

Basic usage

Add one <wr-form-error key> per validator; errors show once the control is touched or dirty.

We'll never share it.
<wr-form-field label="Email" hint="We'll never share it." required>
  <input wrInput [formControl]="email" type="email" />
  <wr-form-error key="required">Email is required.</wr-form-error>
  <wr-form-error key="email">That isn't a valid email.</wr-form-error>
</wr-form-field>

Messages without markup

Without <wr-form-error>, the field shows the rule's { message }, else validation.<key> from your i18n catalog, else nothing.

<!-- Messages come from validation.required, .minlength and .min in your catalog. -->
<wr-form-field label="Handle" required>
  <input wrInput formControlName="handle" />
</wr-form-field>

<wr-form-field label="Age">
  <input wrInput formControlName="age" type="number" />
</wr-form-field>

A message on the validator

Signal Forms rules take { message }. A custom validator puts a message string on its error.

import { Component, signal } from '@angular/core';
import { FormField, email, form, required } from '@angular/forms/signals';
import { WrFormField } from 'ngwr/form';
import { WrInput } from 'ngwr/input';

@Component({
  imports: [FormField, WrFormField, WrInput],
  template: `
    <wr-form-field label="Work email" required>
      <input wrInput type="email" [formField]="signup.email" />
    </wr-form-field>
  `,
})
export class Signup {
  private readonly model = signal({ email: '' });

  protected readonly signup = form(this.model, path => {
    required(path.email, { message: 'Enter your work email.' });
    email(path.email, { message: 'That is not an email address.' });
  });
}

A message in your catalog

A validation.<key> entry covers that error on every field; minLength and maxLength read the minlength and maxlength keys.

provideWrI18nStaticLoader({
  en: {
    validation: {
      required: 'This field is required.',
      // Filled from the error payload: { requiredLength, actualLength }.
      minlength: 'Minimum length is {{requiredLength}}.',
      min: 'Enter {{min}} or more.',
    },
  },
  ru: {
    validation: {
      required: 'Обязательное поле.',
      minlength: 'Не короче {{requiredLength}} символов.',
      min: 'Не меньше {{min}}.',
    },
  },
}),

A message in the markup

<wr-form-error key> wins for its key on that field. Other keys still resolve.

<wr-form-field label="Handle" required>
  <input wrInput formControlName="handle" />
  <!-- Only for required; minlength still resolves. -->
  <wr-form-error key="required">Pick a handle.</wr-form-error>
</wr-form-field>

Optional marker

optional marks a field users can skip.

Up to 140 characters.
<wr-form-field label="Bio" optional hint="Up to 140 characters.">
  <input wrInput [formControl]="bio" placeholder="A short tagline" />
</wr-form-field>

Required marker

required only draws the asterisk. It adds no validator, and a validator does not set it: write both.

<!-- Validators.required on the control, required on the field. -->
<wr-form-field label="Email" required>
  <input wrInput formControlName="email" type="email" />
</wr-form-field>

<!-- Or read it off the control; requiredValidator is Validators.required. -->
<wr-form-field label="Email" [required]="form.controls.email.hasValidator(requiredValidator)">
  <input wrInput formControlName="email" type="email" />
</wr-form-field>

API

NameDescriptionTypeDefault
labelLabel text shown above the projected control.string''
optionalLabelWord inside the optional marker. Defaults to the form.optional catalog key.string | nullnull
autoErrorsShow a message for each error no <wr-form-error> claims. Turn off to write every message by hand.booleantrue
hintSubtext under the control. Hidden when an error is visible.string''
requiredShow a red * next to the label.booleanfalse
optionalShow (optional) next to the label. Ignored if required is on.booleanfalse
controlIdSet the <label for> target manually. Auto-generated otherwise.stringauto
<wr-form-error>One message per validator key. Shows when the control is touched or dirty and has that error.component—

See also