An Angular Upgrade Service?

Earlier this month, I polled people on LinkedIn to know which version of Angular they’re currently using at work. Here are the results:

First, I was pleasantly surprised that most of them (58%) are fully up-to-date or just one major version behind. What’s more worrying is that 24% of them use a non-supported version. So I asked why people use older versions of the framework (you can contribute here with your comments if you want), and of course, I knew more or less what the answers would be: Time – Budget – Dependencies.

This made me think about the following: What if there was an Angular Upgrade Service? A hands-on service you could subscribe to that would keep your projects up-to-date. Do you think that would be of interest?

I’m always looking for gaps to fill in the front-end development space. This is why I created the Angular certification program, the React certification program, and just helped launch a Javascript certification program!)

In any case, if you think you could use an Angular Upgrade Service, please let me know (respond to that email or reach out on LinkedIn – always happy to connect!), and I might beta-test that idea with you in the next quarter.

Syntax tricks for Angular HTML attribute bindings

What’s great about Angular is that the framework provides many different features out of the box. Some are well documented and widely known, while others are more obscure and less commonly used, though very useful.

For instance, there’s a ngClass directive to dynamically set classes to an element based on conditions:

In the above example, the button will have an active-btn CSS class if isActive is true, and a disabled-btn CSS class if isDisabled is true. That’s what the directive does.

But the thing is… We don’t need a directive to do that. I never use ngClass.

Instead, I do this:

It works the same way, and I’d argue that the syntax is slightly easier to understand. No directive needed.

On a different note, if you’ve ever tried to set the values of non-HTML attributes to an element, such as data-test for unit or end-to-end testing, you would find out that this syntax doesn’t work: [data-test]="value"

For these “unknown” HTML attributes, we need to use the following syntax: [attr.data-test]="value"

And then all is fine.

How to use Jest for Angular Unit Tests?

Karma is deprecated, and the Angular team is working on official support for Jest and Web Test Runner. Today, I want to highlight how to set up your Angular project for Jest since it’s prevalent in the Javascript ecosystem (React, etc.). It’s super fast and 100% browserless, making running tests on a continuous integration server easy.

Also, the syntax is essentially the same as Jasmine/Karma, so you won’t have to change your tests much unless you do extensive mocking in Jasmine.

Here are the steps I’ve used on over 28 repositories so far, with great success:

  1. Uninstall Jasmine, Karma, and all associated types
    npm uninstall karma karma-chrome-launcher karma-coverage karma-jasmine karma-jasmine-html-reporter @types/jasmine jasmine-core

  2. Install Jest, its types, and presets for Angular
    npm i --save-dev jest @types/jest jest-preset-angular

  3. Create a setup-jest.ts file in your project’s root folder
    And add that single line of code in it:
    import 'jest-preset-angular/setup-jest';

  4. Create a jest.config.ts file in your project’s root folder with the following command
    npx jest — init

    Then ensure you have the following config options set in that file:
    preset: 'jest-preset-angular',
    setupFilesAfterEnv: ['./setup-jest.ts']


  5. Update your tsconfig.spec.json file

    Your compilerOptions should look like this:
    "compilerOptions": {
    "outDir": "./out-tsc/spec",
    "types": [ "jest" ],
    "esModuleInterop": true,
    "emitDecoratorMetadata": true
    },


  6. Last step: Update package.json
    In the scripts section, replace the test entry with:
    "test": "jest",

And… that’s it! You can now launch npm run test to run your tests with Jest. Sure, a schematic would be better, but those steps take only 2-3 minutes to implement for now.

How to use toSignal() outside of an injection context?

If you’ve ever tried to convert an Observable into a Signal using the toSignal() function, you might have encountered the following error NG0203: toSignal() can only be used within an injection context:

This happens when you create an Observable outside of an injection context, which can happen if you want to download data when the user clicks on a button, for instance:

Can we make this work? Absolutely! The key is to access our injector and store it in a variable so we can pass it as a parameter to toSignal(). How to do that?

Interestingly enough, we can access our injector by… injecting it into our component:

Then, we pass it as an option to the toSignal() function:

And that’s it, problem solved! You can see the full code example on Stackblitz.

How to migrate Angular syntax to the latest features?

I posted about how to update your version of Angular a while back. Still, with the ongoing updates in the framework, such as standalone components, the new control flow syntax, and the inject() function, there are quite a few new options that aren’t required but can still be adopted widely.

For instance, if you want to remove your ngModules and go full-standalone; there’s a migration command for that:

This command will ask you about different options, such as removing unnecessary ngModules, and switching your AppComponent to standalone bootstrapping.

If you want to get rid of NgFor, NgIf, and the like, you can migrate automatically to the new control flow syntax with:

Another migration command was added in Angular 18.2. This one is to migrate your dependency injection syntax to use inject() instead of constructors:

Finally, and also added in Angular 18.2, is the migration to use lazy-loading on all routes:

This last command can be applied to a subset of routes by using the path argument:

The official documentation for all these migrations can be found here.

When you don’t need template-driven or reactive forms

A common misconception shared by many Angular developers is that if you have a form, you need to use template-driven or reactive forms to handle it.

The truth is, in most cases, simple forms don’t need any of that. Instead, you can use template reference variables. My favorite example is a login form:

The two template reference variables used in the above example are sufficient to capture the username and password when the user clicks the log-in button. There is no need for extra complexity!

We lose some features along the way, such as reactivity to changes and automatic form validation. For instance, if I use this expression, the value will not change unless the component gets refreshed by another change:

This is because no Angular event listener triggers change detection when the form input value changes. But again, for my example of the log-in form, it doesn’t matter.

You can check out a live example here where I capture updates when the user clicks a button to emulate some reactivity. For anything more complex than such an example, using template-driven or reactive forms makes perfect sense.

If you want to learn more about similar tips and tricks through code challenges delivered to your inbox twice a week, check out the Angular Accelerator program. It’s currently open for 5 free scholarships, and there’s a 5-day free trial no matter what!

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.