Press enter or click to view image in full size

Signal Forms are available with Angular 21 as an experiment, which means the API described in this tutorial is subject to change, but stable enough to give you a first overview of Angular Signal Forms.

Rather than replacing template-driven forms or reactive forms, signal forms are a third option. In this brief tutorial, I will highlight how the new paradigm works and what the differences are compared to previous approaches.

Creating a signal form

Signal forms are driven by signals, and signals are driven by data. This means that to create a signal form, we start by describing the data we want our form to manage:

  userInfo = signal({
firstName: "",
lastName: "",
street: "",
zip: "",
city: "",
cc: ""
});

Then, we tell Angular that we want to manage that data using a form:


userForm = form(this.userInfo);

Note that all signal form features come from the same module @angular/forms/signals, which means all TypeScript imports look like this:

import {Field, form} from '@angular/forms/signals';

The last step is to connect our form to an HTML template using the Field directive as follows:

 




And that’s it! You don’t need to add a name attribute, as was the case with template-driven forms. Angular forms will add that name for you. Not only that, if you do add a name attribute, the compiler will tell you not to do that:

Press enter or click to view image in full size

And now we have a signal form. What this means is that userForm exposes an API similar to what we’re used to with reactive forms, albeit in a funny manner. Here’s what I mean by that:

// We can access all individual fields from our form
this.userForm.firstName().value().set("test");
// We can access the state of individual fields
// Such as valid / pristine / touched / disabled / errors, and more!
this.userForm.zip().dirty();
// And we can also acccess that information on the entire form
this.userForm().value(); // Returns the entire form value
this.userForm().valid(); // Returns true or false

That’s because this.userForm returns a FieldTree object, whereas this.userForm() returns a FieldState object. The difference is subtle, but the FieldTree allows us to navigate within our form hierarchy to find what we need, and the FieldState returns signals of the field information.

Now, the good news is that everything you could ever dream of is available in those objects, which means displaying feedback to the user looks like this:

  @if(userForm.zip().invalid() && userForm.zip().dirty()) {

Please enter a 5-digit zipcode


}

Adding some form validation

When creating our form, remember that we call the form function like so:

userForm = form(this.userInfo);

Now, if we want to add some validation rules, we pass a second argument to that function, which is a function where we can add validation rules as follows:

userForm = form(this.userInfo, (path) => {
required(path.firstName);
pattern(path.zip, new RegExp("[0-9]{5}"));
maxLength(path.cc, 16);
});

In the above, firstName is required, zip must be a 5-digit number (regular expression validation), and the credit card number (cc) must have a maximum of 16 digits. Any user input that breaks these rules would update the state of the valid() and errors() signals of the related fields.

What’s nice about that approach is that validation rules can be stored in a central location and reused in various forms.

For instance, I can create a creditCardValidation function like so:

function validateCreditCardNumber(field) {
required(field, 16);
minLength(field, 16);
maxLength(path.cc, 16);
}

And then use it in my form like so:

userForm = form(this.userInfo, (path) => {
required(path.firstName);
pattern(path.zip, new RegExp("[0-9]{5}"));
validateCreditCardNumber(path.cc);
});

So that’s really cool to refactor and reuse validation rules. To be fair, this was doable with reactive and template-driven forms, but perhaps not as easily.

Overall, signal forms are promising. Some features seem to be missing (no more CSS ng- classes applied to form elements) for now, but the setup and API access look easier and more intuitive than before.

We’ll see how that experiment evolves over time, and I’ll cover these updates on the blog. Stay tuned!

My name is Alain Chautard. I am a Google Developer Expert in Angular and a consultant and trainer at Angular Training, where I help web development teams learn and become proficient with Angular / React / JavaScript.

If you need any help learning web technologies, feel free to get in touch!

If you enjoyed this article, please clap for it or share it. Your help is always appreciated. You can also subscribe to my articles and Weekly Angular Newsletter to receive helpful weekly tips and updates about Angular.

Share.
Leave A Reply