I tried the Angular Standalone migration, and here is the result

profile
Tim Deschryver
timdeschryver.dev

Last week Minko Gechev tweeted about an Angular schematic to automate the migration from @NgModules to the standalone API. Of course, I had to try out this migration myself.

https://twitter.com/mgechev/status/1621307204002512897

To test the migration I created a small Angular application, which I will use as a starting point for the migration. While the application is small, it contains a little bit of everything, child components, eager and lazy loaded modules, a pipe, a directive, a couple of tests, and a service.

The schematic is available starting from Angular version 15.2.0-next.2. To update to this version or a later version, run the following command:

You can take a look at the before branch on GitHub if you're interested in the code from the example project.

To run the migration open a new CLI terminal at the root of the Angular project and run the @angular/core:standalone schematic:

This gives you three options:

  1. Convert all components, directives, and pipes to standalone
  2. Remove unnecessary NgModule classes
  3. Bootstrap the application using standalone APIs

To complete the migration, you need to run all three options. Instead of migrating your whole codebase at once, you can also run the schematic on specific directories.

The first time I ran the schematic I tried to keep the application running and the tests green. For this, I had to manually update some parts of the code and tests (see the steps below). But while running the next options, I noticed that the schematic was also fixing some of the issues I had to fix manually. That's why I decided to run the schematic from the start again, but this time I ran the schematics after each other without updating the code and tests. Looking back at it, I think the latter is the way to go, although it seems not to be recommended in the Angular docs.

The schematic only migrates the code from NgModules to the new standalone API syntax. But, lately Angular also added a bunch of new functional APIs. For the completeness of this migration, I also manually migrated some features that are not covered by the schematic to their new equivalent functional API version.

  1. Migrate to provideRouter
  2. Migrate to provideHttpClient
  3. Migrate to functional router guards
  4. Update tests, only import standalone components

If you're not interested in the details, you can take a look at the migrated version on the main branch (with manual changes between migration steps) or on the after branch (all migrations at once, and manual changes afterward).

Why you should migrate link

I think you should migrate to the standalone components because it has a few benefits.

The foremost is that Angular has a smoother learning curve for new developers. For new and experienced developers, a big advantage is a simplified codebase, which is easier to understand and maintain.

It also has a few performance benefits, e.g. you can lazy load a component because it defines its own dependencies explicitly.

Another benefit is that your tests require less setup code. In most cases, you only need to import the component you want to test and mock the external dependencies e.g. an HTTP service.

And who knows, perhaps somewhere in the future that Angular can automagically import the dependencies for you, and is step this step just an intermediate step to make that possible. But for now, you need to do it manually.

From the docs docs:

format_quote

Standalone components provide a simplified way to build Angular applications. Standalone components, directives, and pipes aim to streamline the authoring experience by reducing the need for NgModules. Existing applications can optionally and incrementally adopt the new standalone style without any breaking changes.

1. Convert all components, directives, and pipes to standalone link

Commit: d32df876bebc4f1824589bca14799cc27d6ff602:

Command link

Results link

Manual changes link

Notes link

Migration Examples link

Components are migrated to standalone components:

NgModules are updated by moving the declarations to the imports:

2. Remove unnecessary NgModule classes link

Commit: c74471ae5b9627ab73ed0e163600834d4d51f85d

Command link

Results link

Manual changes link

Migration Examples link

The file shared.module.ts is deleted because it only contained an NgModule, SharedModule:

NgModuless that reference the removed NgModules are updated.

3. Bootstrap the application using standalone APIs link

Commit: 16c649d64130741ea75e4d35517ffd6b5b80cdc8

Command link

Result link

Manual changes link

Notes link

Migration Examples link

4. Remove unnecessary NgModule classes (for AppModule) link

Commit: c05ca76aad7717e303037e33c269602627ab9720

Command link

Result link

5. Migrate to provideRouter link

Commit: 528661c9cef1e9f3bf5cb83ff6571c96c4ae8164

This is not an automatic migration.

Result link

We can use provideRouter() instead of RouterModule.forRoot() and RouterModule.forChild().

For more info about provideRouter see Angular Router Standalone APIs by Kevin Kreuzer.

Migration Examples link

6. Migrate to provideHttpClient link

Commit: 655217f3f528fc7db83515cfce59275043dd6183

This is not an automatic migration.

Result link

Instead of importing HttpClientModule in AppModule, and registering interceptors as providers with HTTP_INTERCEPTORS we can now use provideHttpClient().

For more info about provideHttpClient see The Refurbished HttpClient in Angular 15 – Standalone APIs and Functional Interceptors by Manfred Steyer.

Migration Examples link

7. Migrate to functional router guards link

Commit: 6b1977d24e9770871f432b0eaa0e24efd94d41fe

This is not an automatic migration.

Result link

A router guard that was implemented as a class can be refactored to a function.

For more info about functional router guards see How To Use Functional Router Guards in Angular by Dany Paredes . It's probably best to immediately migrate to the new canMatch guard, for more info see Introducing the CanMatch Router Guard In Angular by Netanel Basal.

Migration Examples link

Before:

After:

8. Update tests, only import standalone components link

Commit: 7e04027511b8ece03522bb3e52e87775e4f7dd8a

This is not an automatic migration.

Result link

Because a component now contains all its dependencies, we can refactor the test cases. The test becomes simpler because we are not required to import all the dependencies anymore. Instead, we can import the component itself.

Migration Examples link

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.

Buy Me a Coffee at ko-fi.com PayPal logo

Share this post