Signal forms in Angular

Home » Tutorials » JavaScript » Signal forms in Angular

Angular form signals are a powerful tool that enables the creation of interactive web applications with elegance and simplicity. By embracing a reactive approach to data handling, they ensure instant updates to the user interface as form values change. This feature allows developers to effortlessly track form states and manage their behavior, resulting in a more predictable and efficient development process. With Angular form signals, you can build highly responsive and dynamic applications, enhancing user interaction with your services while maintaining clean and maintainable code.

In this lesson, we will build a user form using Signal Forms. Through a practical example, we will cover:

  • creating a form model with signal();
  • connecting the model to a form using form();
  • adding built-in validators with required();
  • creating custom validation rules with validate();
  • submitting the form and handling user input.

By the end of the lesson, you will see how Signal Forms can make your code more readable, type-safe, and easier to maintain compared to the traditional Reactive Forms approach.

users.component.ts

import {Component, injectAsync, input, InputSignal, signal} from '@angular/core';
import {form, FormField, required, validate} from '@angular/forms/signals';
import {FormsModule} from '@angular/forms';
import {JsonPipe} from '@angular/common';

interface UserForm {
  userName: string;
  role: 'admin' | 'editor' | 'viewer' | 'moderator';
}

@Component({
  selector: 'app-users',
  imports: [
    FormField,
    FormsModule,
    JsonPipe
  ],
  templateUrl: './users.component.html',
  styleUrl: './users.component.css',
})
export class UsersComponent {

  public users: InputSignal<string[]> = input<string[]>([]);

  protected loginFormModel = signal<UserForm>({
    userName: '',
    role: 'admin',
  })

  protected loginForm = form(this.loginFormModel, (schema) => {
      required(schema.userName, {
        message: 'User name is required',
        error: 'userNameRequired'
      }),
      validate(schema.userName, ({value}) => {
        if(value().trim().length > 5) {
          return {
            kind: 'tooLong',
            message: 'Too long username'
          }
        }
        return null;
      })
    })

  protected usersService = injectAsync(() =>
    import('../users.service').then(s => s.UsersService));

  protected async updateUsers() {
    const users = await this.usersService();

    users.updateUsers();
  }

  protected submitForm(event: Event) {
    event.preventDefault();
    console.log(this.loginFormModel());
    console.log(this.loginForm.userName().valid());
  }
}

users.component.html

<p>users works!</p>

<ul>
  @for (user of users(); track user) {
    <li>{{user}}</li>
  }
</ul>

<button (click)="updateUsers()">Update users</button>


<form (submit)="submitForm($event)">
  <p>
    <input type="text" [formField]="loginForm.userName">
  </p>

  @if(loginForm.userName().touched() && loginForm.userName().invalid()) {
    <p>Username is invalid</p>
    <p>{{loginForm.userName().errors() | json}}</p>
  }
  <p>

  </p>

  <p>
    <select>
      <option value="admin">Admin</option>
      <option value="editor">Editor</option>
      <option value="viewer">Viewer</option>
      <option value="moderator">Moderator</option>
    </select>
  </p>

  <button>Submot form</button>
</form>

<hr>

Username: {{loginForm.userName().value() | json}}
Username touched: {{loginForm.userName().touched()}}
Username valid: {{loginForm.userName().valid()}}


Role: {{loginForm.role().value() | json}}

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *


The reCAPTCHA verification period has expired. Please reload the page.

Pin It on Pinterest

Share This