Angular Signal Forms Keeps Improving (v21.2)

Series: Angular Signal Forms (2/2)
- Refactoring a form to a Signal Form
- Angular Signal Forms Keeps Improving (v21.2)
In the previous blog post "Refactoring a form to a Signal Form", we refactored an existing Angular form to use the new Signal Forms feature. At the time, Signal Forms were still experimental, and we mentioned that there were some limitations and areas for improvement.
Since then, the Angular team has continued to improve Signal Forms. In this blog post, we will take a look at some of the improvements that were added in recent versions of Signal Forms (at the time of writing v21.2), including application-wide CSS classes, programmatic focus management, and a more structured submission flow.
provideSignalFormsConfig
To start with, there's a new method provideSignalFormsConfig to configure the Signal Forms in an application.
Currently, this method allows us to set CSS classes through the classes option, which are applied to form controls based on their state.
This can be used to easily apply consistent styling across form controls, e.g. to re-apply the ng-* classes that were present in the previous version of Angular forms.
The classes option accepts an object where the keys are the CSS class names and the values are functions that receive the FormField to determine when to apply the class based on the form control's state. Of course, this is not limited to re-applying the ng-* classes. You can define any custom classes that you want to apply based on the form control's state.
NG_STATUS_CLASSES
If you want to re-apply all the ng-* classes, Angular provides a predefined set of classes called NG_STATUS_CLASSES that you can use with provideSignalFormsConfig to have the same behavior as the previous version of Angular forms. This is a great option for those who want to migrate to Signal Forms without losing the styling.
focusBoundControl
Another improvement is the addition of the focusBoundControl method on the FormField instance.
With focusBoundControl, we can programmatically focus the first form control (based on the DOM order) within a field tree.
This only works when the form control is already rendered in the DOM. If there are no elements to be focused, the method does nothing.
We use this method to focus the first form control when a dialog is opened.
It also opens up new possibilities for improving the user experience, especially when it comes to form validation. For example, we can use this method to focus the first invalid form control.
To do that, we can use the errorSummary on the form instance to get a summary of all the errors in the form. Then, we can call focusBoundControl on the first field tree to focus the first invalid form control.
Please keep in mind that focusing an input automatically can cause accessibility issues, so ensure it enhances the user experience rather than detracting from it.
Submission API
The third improvement that we will discuss in this blog post is the new Submission API. The Submission API provides a more structured way to handle form submissions, including both valid and invalid form states.
You can implement an asynchronous submission action using the submission option in the form function. The submission option accepts an object with an action property that is called when a valid form is submitted.
The action receives the form instance as an argument, which you can use to access the form value and perform any necessary operations, such as making an API call to submit the form data. This action marks all the fields in the form as touched, so you don't have to worry about that in your implementation. The action returns a TreeValidationResult, which can be used to set validation errors on the form based on the response from the server.
The submission option also accepts an onInvalid property that is called when an invalid form is submitted. This can be used to handle any specific logic when the form is invalid. This is great in combination with the focusBoundControl method that we discussed earlier, as we can focus the first invalid form control when the form is submitted in an invalid state. onInvalid receives the form instance as an argument, and does not return anything.
But how do we trigger the submission action?
Instead of implementing a submit event handler, we now set the (new) formRoot directive on the form element to bind the form instance to the form in the template. This automatically handles the form submission (for example, when the user clicks a submit button or presses enter in an input field), and triggers the appropriate submission action based on the form's validity.
formRoot directive
The formRoot directive also adds the novalidate attribute to the form element, which disables the browser's default validation.
The directive needs to be imported from the @angular/forms/signals package in the component's imports array.
Conclusion
These improvements make Signal Forms feel more complete and practical to use in real applications. With provideSignalFormsConfig, focusBoundControl, the Submission API, and the formRoot directive, several rough edges from the earlier versions have already been smoothed out. Signal Forms is clearly evolving quickly, and these additions make it an even more compelling option if you want to build forms with Angular's signal-based APIs.
Outgoing links
Feel free to update this blog post on GitHub, thanks in advance!
Join My Newsletter (WIP)
Join my weekly newsletter to receive my latest blog posts and bits, directly in your inbox.
Support me
I appreciate it if you would support me if have you enjoyed this post and found it useful, thank you in advance.