From b99f8f9a1481237d98dbb995a1d7d2a4f1cafe34 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Mon, 29 Jul 2024 17:34:06 +0200 Subject: [PATCH] docs: add documentation for the inject migration (#57141) Adds a reference for the `inject()` migration to ADEV. PR Close #57141 --- adev/src/app/sub-navigation-data.ts | 5 + .../reference/migrations/inject-function.md | 122 ++++++++++++++++++ .../content/reference/migrations/overview.md | 13 +- 3 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 adev/src/content/reference/migrations/inject-function.md diff --git a/adev/src/app/sub-navigation-data.ts b/adev/src/app/sub-navigation-data.ts index 727118021dc..062aaa97c8c 100644 --- a/adev/src/app/sub-navigation-data.ts +++ b/adev/src/app/sub-navigation-data.ts @@ -1477,6 +1477,11 @@ const REFERENCE_SUB_NAVIGATION_DATA: NavigationItem[] = [ path: 'reference/migrations/control-flow', contentPath: 'reference/migrations/control-flow', }, + { + label: 'inject() Function', + path: 'reference/migrations/inject-function', + contentPath: 'reference/migrations/inject-function', + }, ], }, { diff --git a/adev/src/content/reference/migrations/inject-function.md b/adev/src/content/reference/migrations/inject-function.md new file mode 100644 index 00000000000..e31d2d43ab2 --- /dev/null +++ b/adev/src/content/reference/migrations/inject-function.md @@ -0,0 +1,122 @@ +# Migration to the `inject` function + +Angular's `inject` function offers more accurate types and better compatibility with standard +decorators, compared to constructor-based injection. You can convert your application to it by +running the following command: + + + +ng generate @angular/core:inject + + + +#### Before + +import { Component, Inject, Optional } from '@angular/core'; +import { MyService } from './service'; +import { DI_TOKEN } from './token'; + +@Component() +export class MyComp { + constructor(private service: MyService, @Inject(TOKEN) @Optional() readonly token: string) {} +} + + +#### After + +import { Component, inject } from '@angular/core'; +import { MyService } from './service'; +import { DI_TOKEN } from './token'; + +@Component() +export class MyComp { + private service = inject(MyService); + readonly token = inject(DI_TOKEN, { optional: true }); +} + + +## Migration options +The migration includes several options to customize its output. + +### `path` +Determines which sub-path in your project should be migrated. Pass in `.` or leave it blank to +migrate the entire directory. + +### `migrateAbstractClasses` +Angular doesn't validate that parameters of abstract classes are injectable. This means that the +migration can't reliably migrate them to `inject` without risking breakages which is why they're +disabled by default. Enable this option if you want abstract classes to be migrated, but note +that you may have to **fix some breakages manually**. + +### `backwardsCompatibleConstructors` +By default the migration tries to clean up the code as much as it can, which includes deleting +parameters from the constructor, or even the entire constructor if it doesn't include any code. +In some cases this can lead to compilation errors when classes with Angular decorators inherit from +other classes with Angular decorators. If you enable this option, the migration will generate an +additional constructor signature to keep it backwards compatible, at the expense of more code. + +#### Before + +import { Component } from '@angular/core'; +import { MyService } from './service'; + +@Component() +export class MyComp { + constructor(private service: MyService) {} +} + + +#### After + +import { Component } from '@angular/core'; +import { MyService } from './service'; + +@Component() +export class MyComp { + private service = inject(MyService); + + /** Inserted by Angular inject() migration for backwards compatibility */ + constructor(...args: unknown[]); + + constructor() {} +} + + +### `nonNullableOptional` +If injection fails for a parameter with the `@Optional` decorator, Angular returns `null` which +means that the real type of any `@Optional` parameter will be `| null`. However, because decorators +cannot influence their types, there is a lot of existing code whose type is incorrect. The type is +fixed in `inject()` which can cause new compilation errors to show up. If you enable this option, +the migration will produce a non-null assertion after the `inject()` call to match the old type, +at the expense of potentially hiding type errors. + +**Note:** non-null assertions won't be added to parameters that are already typed to be nullable, +because the code that depends on them likely already accounts for their nullability. + +#### Before + +import { Component, Inject, Optional } from '@angular/core'; +import { TOKEN_ONE, TOKEN_TWO } from './token'; + +@Component() +export class MyComp { + constructor( + @Inject(TOKEN_ONE) @Optional() private tokenOne: number, + @Inject(TOKEN_TWO) @Optional() private tokenTwo: string | null) {} +} + + +#### After + +import { Component, inject } from '@angular/core'; +import { TOKEN_ONE, TOKEN_TWO } from './token'; + +@Component() +export class MyComp { + // Note the `!` at the end. + private tokenOne = inject(TOKEN_ONE, { optional: true })!; + + // Does not have `!` at the end, because the type was already nullable. + private tokenTwo = inject(TOKEN_TWO, { optional: true }); +} + diff --git a/adev/src/content/reference/migrations/overview.md b/adev/src/content/reference/migrations/overview.md index 04f3524352f..46508227d6d 100644 --- a/adev/src/content/reference/migrations/overview.md +++ b/adev/src/content/reference/migrations/overview.md @@ -5,12 +5,12 @@ Learn about how you can migrate your existing angular project to the latest feat Standalone components provide a simplified way to build Angular applications. Standalone components specify their dependencies directly instead of getting them through NgModules. - + Standalone components, directives, and pipes aim to streamline the authoring experience by reducing the need for NgModules. - ModuleWithProviders has been optional since Angular version 7. But with Ivy the metadata present in metadata.json is no longer required, instead Ivy relies on the generic type for ModuleWithProviders to get the correct type information. - + ModuleWithProviders has been optional since Angular version 7. But with Ivy the metadata present in metadata.json is no longer required, instead Ivy relies on the generic type for ModuleWithProviders to get the correct type information. + Angular version 9 deprecates ModuleWithProviders without a generic type. A future version of Angular will remove the default generic type, making an explicit type required. @@ -18,7 +18,10 @@ Learn about how you can migrate your existing angular project to the latest feat Strictly typed reactive forms add type safety and the types enable a variety of other improvements, such as better autocomplete in IDEs, and an explicit way to specify form structure. - - Control Flow Syntax is available with Angular 17 release and allows you to use more ergonomic syntax which is close to javascript, better type checking and lazy load part of the component. It replaces the need to imports CommonModule to use functionalities like *ngFor,`*ngIf. + + Control Flow Syntax is available with Angular 17 release and allows you to use more ergonomic syntax which is close to javascript, better type checking and lazy load part of the component. It replaces the need to imports CommonModule to use functionalities like `*ngFor`, `*ngIf`. + + + Angular's `inject` function offers more accurate types and better compatibility with standard decorators, compared to constructor-based injection.