FormGroup: All you need to know

Recently, during an Angular Accelerator coaching call, my client asked what is the point of using FormGroup. I then realized I had never written about FormGroup, so here is a post to fix that.

When using reactive forms, we always have at least one FormGroup, which happens to be the entire form. This doesn’t mean that Form Groups are only designed for that purpose: A complex form can be divided into multiple groups. For instance, if we have a form where a user is supposed to enter their name, address, birth date, etc., we could group some of these controls into FormGroups.

What do we get out of that? A few different things:

  1. Form groups aggregate the validation state of all individual controls into a group state. If an address is made of 4 controls (street, city, zip code, country), then the form group for these 4 controls will be invalid when any controls are invalid, touched when any control is touched, etc.
  2. Form groups aggregate actions on all individual controls within the group. Want to disable address entry? Instead of disabling all 4 controls one by one, you can do addressGroup.disable() , which is a lot less error-prone (you can’t forget one of these controls)
  3. Form groups store the value of all controls as a single object. In my example with 4 controls for an address, accessing addressGroup.getValue() would return:

So, the main benefit is to have grouped actions and status updates for a bunch of form controls. Let’s take another example with actual code (you can find the full code base on Stackblitz here):

If I use the following expression in my template to see the value of my userInfo, here’s what I get:

As you can see, my form group creates an object that updates when individual control values change. Perfect! Now, let’s click a button that sets the form group value to an object:

Now, I can use multiple values with one line of code. There is no need to call setValue() on all controls one by one. That’s cool. What if I want to apply a “diff” to my group and not set the entire thing? Say I wanted to be younger but not change my name or anything else:

Now, I can “patch” my form group with some localized changes, and I don’t need to worry about the other values of the controls of that group. That’s the magic of formGroup.patchValue().

You can also reset the entire group with formGroup.reset(), add or remove controls and validators, and more. The full API is here. That’s the power of FormGroups in action!

What’s new in Angular 18.1?

Last week, I covered the new @let syntax to create local variables in our HTML templates.

Let’s review some other notable updates from Angular 18.1.

toSignal() custom equality function

Remember the toSignal function to convert an Observable into a Signal? This function now supports an equal parameter to pass a custom equality function. You can read this tutorial on Signal equality functions for more information and examples.

RouterLink with UrlTree

You’ve probably used UrlTree objects in Angular route guards to implement redirects when a user isn’t logged in, for instance. It is now possible to use UrlTree with RouterLink, too, so you can do something like this:

After defining userPath as a UrlTree as follows:

Diagnostic for uninvoked functions

An added diagnostic throws an Angular error if you make a mistake in your template syntax. If you do (click)="save" instead of (click)="save()".

There are some other changes under the hood and some updates to unstable APIs, but I’ll cover those when the changes are finalized and become stable.

@let for local variables in Angular views

Angular 18.1 brought a new feature to our Angular templates: the ability to create local variables. Just like the new control-flow blocks @if, @for, and @switch, the new syntax uses the @ character with a new keyword: @let.

Here is an example:

The above code displays 21 as the value of the Angular expression. Why is this helpful? In many cases, we deal with complex nested objects that translate into long, verbose expressions:

With @let, we can make things shorter:

Another major use case is when using the async pipe. I covered several tricks on how to use the async pipe in the past, even introducing ngrxLet as an alternative, and all of these tricks can now be replaced with @let.

Here is the initial problem with async when dealing with data from an Observable that we need in several places:

The above syntax is not only horrible, but it also multiplies subscriptions to the source Observable, which is even worse. The common workaround consisted of using a structural directive because such directives allow using local variables as follows:

That’s more readable but still not ideal. Here is what we can do with @let now:

That’s a lot better. If the multiple ? drive you crazy, you can do this instead:

@let variables are read-only and cannot be reassigned. They behave like the const keyword in Javascript. The scope is also to the nearest block (which means view, in the case of Angular templates), so in my above example, addr is only available within the @if block, invisible outside of it.

You can play with my examples on Stackblitz here.

3 pieces to discover or read again this summer

It’s early July, and in several countries, it’s a time for vacation, relaxation, and even celebration, like for us in the United States, with the 4th of July holiday to celebrate the country’s declaration of independence.

As a result, it’s a good time to reflect on the content published during the first half of the year.

Here are my three most popular posts of 2024 so far – feel free to discover them or reread them, as there’s a lot of important content in those three:

  1. Angular signal-based components tutorial: No surprise, as this is the future of Angular applications. If you’re more into video content and live coding, you can watch this recording of my workshop at FrontEnd Nation in June: Part 1Part 2.
  2. Introduction to server-side rendering with Angular. Server-side rendering has never been this easy with Angular, so I always recommend looking into it and being curious about it.
  3. How to use NgRx SignalStore? Signals are all the rage now, and NgRx released a state-management feature built around signals. The learning curve is much better than that of regular NgRx.