Examples Simple Form

Simple Form

Login form example

Email is required

Password is required

Form Inspector

Field Tree

(root) valid: false | invalid: true | pending: false
value: {
  "email": "",
  "password": ""
}
touched: false
dirty: false
hidden: false
readonly: false
disabled: false
disabledReasons: (0)
[]
errors: (0)
[]
errorSummary: (2)
[
  {
    "kind": "required",
    "message": "Email is required"
  },
  {
    "kind": "required",
    "message": "Password is required"
  }
]
meta: {
  "pattern": [],
  "required": false
}
            

Children

email valid: false | invalid: true | pending: false
value: ""
touched: false
dirty: false
hidden: false
readonly: false
disabled: false
disabledReasons: (0)
[]
errors: (1)
[
  {
    "kind": "required",
    "message": "Email is required"
  }
]
errorSummary: (1)
[
  {
    "kind": "required",
    "message": "Email is required"
  }
]
meta: {
  "pattern": [],
  "required": true
}
            

Children

— none —

password valid: false | invalid: true | pending: false
value: ""
touched: false
dirty: false
hidden: false
readonly: false
disabled: false
disabledReasons: (0)
[]
errors: (1)
[
  {
    "kind": "required",
    "message": "Password is required"
  }
]
errorSummary: (1)
[
  {
    "kind": "required",
    "message": "Password is required"
  }
]
meta: {
  "pattern": [],
  "required": true
}
            

Children

— none —

TypeScript
HTML
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { customError, email, Field, form, required, schema, submit } from '@angular/forms/signals';
import { delay, firstValueFrom, of } from 'rxjs';
import { FormInspectorComponent } from '../../../ui/form-inspector.ts/form-inspector';
import { DemoLayout } from '../../../ui/demo-layout/demo-layout';

interface LoginForm {
  email: string;
  password: string;
}

@Component({
  selector: 'simple-form',
  standalone: true,
  templateUrl: './simple-form.html',
  imports: [Field, FormInspectorComponent, DemoLayout],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SimpleForm {
  protected readonly loginForm = form(
    signal<LoginForm>({ email: '', password: '' }),
    schema<LoginForm>((schemaPath) => {
      required(schemaPath.email, { message: 'Email is required' });
      required(schemaPath.password, { message: 'Password is required' });
      email(schemaPath.email, { message: 'Invalid email format' });
    }),
  );

  protected submitForm(event: Event) {
    event.preventDefault();

    submit(this.loginForm, async (form) => {
      try {
        await firstValueFrom(of(form().value()).pipe(delay(4000)));
        return undefined;
      } catch (error) {
        return customError({ message: 'Submission failed. Please try again.' });
      }
    });
  }
}