docs: update injectable migration guide to reflect latest schematic changes (#33960)
Since we created the migration guide for the `missing-injectable` schematic, the schematic changed in various ways. e.g. the migration no longer migrates classes passed to `useExisting` Additionally the migration has been expanded to handle another Ivy breaking change where providers like `{provide: X}` will be intepreted as `{provide: X, useClass: X}`. This pattern should be documented in the migration guide. PR Close #33960
This commit is contained in:
parent
48a1acab52
commit
3414760bdd
|
@ -1,64 +1,65 @@
|
||||||
# Migration to Add Missing `@Injectable()` Decorators
|
# Migration to Add Missing `@Injectable()` Decorators
|
||||||
|
|
||||||
## What does this schematic do?
|
### What does this schematic do?
|
||||||
|
|
||||||
This schematic adds an `@Injectable()` decorator to a class
|
1. This schematic adds an `@Injectable()` decorator to classes which are provided in the
|
||||||
if the class has been added as a provider anywhere in the application.
|
application but are not decorated.
|
||||||
|
2. The schematic updates providers which follow the `{provide: SomeToken}` pattern
|
||||||
|
to explicitly specify `useValue: undefined`.
|
||||||
|
|
||||||
|
**Example for missing `@Injectable()`**
|
||||||
|
|
||||||
An example diff might look like the following:
|
_Before migration:_
|
||||||
|
```typescript
|
||||||
|
export class MyService {...}
|
||||||
|
export class MyOtherService {...}
|
||||||
|
export class MyThirdClass {...}
|
||||||
|
export class MyFourthClass {...}
|
||||||
|
export class MyFifthClass {...}
|
||||||
|
|
||||||
**Before:**
|
@NgModule({
|
||||||
|
providers: [
|
||||||
```ts
|
MyService,
|
||||||
export class TypeCase {...}
|
{provide: SOME_TOKEN, useClass: MyOtherService},
|
||||||
|
// The following classes do not need to be decorated because they
|
||||||
|
// are never instantiated and just serve as DI tokens.
|
||||||
|
{provide: MyThirdClass, useValue: ...},
|
||||||
|
{provide: MyFourthClass, useFactory: ...},
|
||||||
|
{provide: MyFifthClass, useExisting: ...},
|
||||||
|
]
|
||||||
|
})
|
||||||
```
|
```
|
||||||
|
_After migration:_
|
||||||
**After:**
|
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TypeCase {...}
|
export class MyService {...}
|
||||||
|
@Injectable()
|
||||||
|
export class MyOtherService {...}
|
||||||
|
export class MyThirdClass {...}
|
||||||
|
export class MyFourthClass {...}
|
||||||
|
export class MySixthClass {...}
|
||||||
|
|
||||||
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note that `MyThirdClass`, `MyFourthClass` and `MyFifthClass` do not need to be decorated
|
||||||
|
with `@Injectable()` because they are never instantiated, but just used as a [DI token][DI_TOKEN].
|
||||||
|
|
||||||
There are a few cases where the decorator won't be added. For example:
|
**Example for provider needing `useValue: undefined`**
|
||||||
|
|
||||||
- It already has another decorator such as `@Component()`, `@Directive()` or `@Pipe()`. These decorators already cause the compiler to generate the necessary information.
|
This example shows a provider following the `{provide: X}` pattern. The provider needs to be
|
||||||
- The provider definition has `useValue`, `useFactory`, or `useExisting`. In
|
migrated to a more explicit definition where `useValue: undefined` is specified.
|
||||||
these cases, the framework doesn't need the `@Injectable()` decorator to create the class
|
|
||||||
because it can just use the value,
|
|
||||||
factory function, or existing instance that was provided.
|
|
||||||
|
|
||||||
For example, for the following module definition, the schematic will check
|
_Before migration_:
|
||||||
`TypeCase`, `ProvideCase`, `ExistingClass`, and `SomeClass` to ensure they
|
```typescript
|
||||||
are marked with the `@Injectable()` decorator and add one if not.
|
{provide: MyToken}
|
||||||
|
```
|
||||||
|
_After migration_:
|
||||||
|
```typescript
|
||||||
|
{provide: MyToken, useValue: undefined}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Why is adding `@Injectable()` necessary?
|
||||||
```ts
|
|
||||||
@NgModule({
|
|
||||||
providers: [
|
|
||||||
// TypeCase needs @Injectable()
|
|
||||||
TypeCase,
|
|
||||||
// ProvideCase needs @Injectable()
|
|
||||||
{provide: ProvideCase},
|
|
||||||
// No @Injectable() needed because the value will be used
|
|
||||||
{provide: ValueCase, useValue: 0},
|
|
||||||
// No @Injectable() needed because factory will be used
|
|
||||||
{provide: FactoryCase, useFactory: ()=> null},
|
|
||||||
// ExistingClass needs @Injectable()
|
|
||||||
{provide: ExistingToken, useExisting: ExistingClass},
|
|
||||||
// SomeClass needs @Injectable()
|
|
||||||
{provide: ClassToken, useClass: SomeClass},
|
|
||||||
// No @Injectable() needed because it has a @Pipe() decorator
|
|
||||||
PipeCase,
|
|
||||||
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Why is this migration necessary?
|
|
||||||
|
|
||||||
In our docs, we've always recommended adding `@Injectable()`
|
In our docs, we've always recommended adding `@Injectable()`
|
||||||
decorators to any class that is provided or injected in your application.
|
decorators to any class that is provided or injected in your application.
|
||||||
|
@ -71,15 +72,39 @@ to strictly require the decorator because doing so enables further
|
||||||
optimization of both the compiler and the runtime. This schematic
|
optimization of both the compiler and the runtime. This schematic
|
||||||
adds any `@Injectable()` decorators that may be missing to future-proof your app.
|
adds any `@Injectable()` decorators that may be missing to future-proof your app.
|
||||||
|
|
||||||
|
### Why is adding `useValue: undefined` necessary?
|
||||||
|
|
||||||
|
Consider the following pattern:
|
||||||
|
|
||||||
## When should I be adding `@Injectable()` decorators to classes?
|
```typescript
|
||||||
|
@NgModule({
|
||||||
|
providers: [{provide: MyService}]
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
Any class that is provided or injected somewhere must have an `@Injectable()` decorator. The decorator is necessary for the framework to properly create an instance of that class through DI.
|
Providers using this pattern will behave as if they provide `MyService` as [DI token][DI_TOKEN]
|
||||||
|
with the value of `undefined`. This is not the case in Ivy where such providers will be interpreted
|
||||||
|
as if `useClass: MyService` is specified. This means that these providers will behave differently
|
||||||
|
when updating to version 9 and above. To ensure that the provider behaves the same as before, the
|
||||||
|
DI value should be explicitly set to `undefined`.
|
||||||
|
|
||||||
However, as noted above, classes that already have another class decorator like `@Pipe` do not need both decorators. The existing class decorator will cause the compiler to generate the proper information.
|
### When should I be adding `@Injectable()` decorators to classes?
|
||||||
|
|
||||||
|
Any class that is provided must have an `@Injectable()` decorator. The decorator is necessary
|
||||||
|
for the framework to properly create an instance of that class through DI.
|
||||||
|
|
||||||
## Should I update my library?
|
However, classes which are already decorated with `@Pipe`, `@Component` or `@Directive` do not
|
||||||
|
need both decorators. The existing class decorator already instructs the compiler to generate the
|
||||||
|
needed information.
|
||||||
|
|
||||||
Yes, if your library has any tokens that are meant to be injected, they should be updated with the `@Injectable()` decorator. In a future version of Angular, a missing `@Injectable()` decorator will always throw an error.
|
### Should I update my library?
|
||||||
|
|
||||||
|
Yes, if your library has any classes that are meant to be injected, they should be updated with
|
||||||
|
the `@Injectable()` decorator. In a future version of Angular, a missing `@Injectable()` decorator
|
||||||
|
will always throw an error.
|
||||||
|
|
||||||
|
Additionally, providers in your library that follow the described `{provide: X}` pattern should be
|
||||||
|
updated to specify an explicit value. Without explicit value, these providers can behave
|
||||||
|
differently based on the Angular version in applications consuming your library.
|
||||||
|
|
||||||
|
[DI_TOKEN]: guide/glossary#di-token
|
Loading…
Reference in New Issue