Skip to content

Commit bd67958

Browse files
kapunahelewongkara
authored andcommitted
docs: add undecorated classes migration faq (angular#32478)
PR Close angular#32478
1 parent ad178c5 commit bd67958

3 files changed

Lines changed: 142 additions & 0 deletions

File tree

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,7 @@ testing/** @angular/fw-test
842842
/aio/content/guide/workspace-config.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
843843
/aio/content/guide/deprecations.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
844844
/aio/content/guide/migration-renderer.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
845+
/aio/content/guide/migration-undecorated-classes.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
845846

846847

847848
# ================================================

aio/content/guide/deprecations.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,9 @@ This includes both packages: `@angular/platform-webworker` and
397397

398398
See the [dedicated migration guide for Renderer](guide/migration-renderer).
399399

400+
{@a undecorated-classes}
401+
### Migrating undecorated classes
402+
See the [dedicated migration guide for undecorated classes](guide/migration-undecorated-classes).
400403

401404
{@a removed}
402405
## Removed APIs
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Undecorated classes migration (DI)
2+
3+
This section discusses an Angular version 9 schematic that migrates
4+
two inheritance patterns that need to be updated to work with Ivy.
5+
6+
## What does this migration do?
7+
8+
This migration adds an empty `@Directive()` decorator to undecorated
9+
base classes that are extended by either directives or components.
10+
11+
Before:
12+
```ts
13+
export class BaseMenu {
14+
constructor(private vcr: ViewContainerRef) {}
15+
}
16+
17+
@Directive({selector: '[settingsMenu]'})
18+
export class SettingsMenu extends BaseMenu {}
19+
```
20+
21+
After:
22+
```ts
23+
@Directive()
24+
export class BaseMenu {
25+
constructor(private vcr: ViewContainerRef) {}
26+
}
27+
28+
@Directive({selector: '[settingsMenu]'})
29+
export class SettingsMenu extends BaseMenu {}
30+
```
31+
32+
The schematic also copies any inherited directive or component metadata to the derived class.
33+
34+
Before:
35+
```ts
36+
@Component({
37+
selector: 'base-menu',
38+
template: '<div></div>'
39+
})
40+
class BaseMenu {}
41+
42+
export class SettingsMenu extends BaseMenu {}
43+
```
44+
45+
After:
46+
```ts
47+
@Component({
48+
selector: 'base-menu',
49+
template: '<div></div>'
50+
})
51+
class BaseMenu {}
52+
53+
@Component({
54+
selector: 'settings-menu',
55+
template: '<div></div>'
56+
})
57+
export class SettingsMenu extends BaseMenu {}
58+
```
59+
60+
## Why is this migration necessary?
61+
62+
When a class has a `@Directive()` or `@Component()` decorator,
63+
the Angular compiler generates extra code to inject dependencies into
64+
the constructor. When using inheritance, Ivy needs both the parent class
65+
and the child class to apply a decorator to generate the correct code.
66+
67+
You can think of this change as two cases: a parent class is missing a
68+
decorator or a child class is missing a decorator. In both scenarios,
69+
Angular's run-time needs additional information from the compiler.
70+
This additional information comes from adding decorators.
71+
72+
73+
### Decorator missing from parent class
74+
75+
When the decorator is missing from the parent class,
76+
the subclass will inherit a constructor from a class for
77+
which the compiler did not generate special constructor
78+
info (because it was not decorated as a directive).
79+
When Angular then tries to create the subclass,
80+
it doesn't have the correct info
81+
to create it.
82+
83+
In View Engine, the compiler has global knowledge, so it
84+
can look up the missing data. However, the Ivy compiler
85+
only processes each directive in isolation. This means that
86+
compilation can be faster, but the compiler can't
87+
automatically infer the same
88+
information as before. Adding the `@Directive()` explicitly
89+
provides this information.
90+
91+
In the future, add `@Directive()` to base classes that
92+
do not already have decorators and are extended by directives.
93+
94+
### Decorator missing from child class
95+
96+
When the child class is missing the decorator, the
97+
child class inherits from the
98+
parent class yet has no decorators of its own.
99+
Without a decorator, the compiler has no way of knowing
100+
that the class is a `@Directive` or `@Component`, so
101+
it doesn't generate the proper instructions for the directive.
102+
103+
104+
## What does it mean to have a `@Directive()` decorator with no metadata inside of it?
105+
106+
The presence of the `@Directive` decorator causes Angular to generate
107+
extra code for the affected class. If that decorator includes no
108+
properties (metadata),
109+
the directive won't be matched to elements or instantiated
110+
directly, but other classes that _extend_ the
111+
directive class will inherit this generated code. You can think of
112+
this as an "abstract" directive.
113+
114+
Adding an abstract directive to an `NgModule` will cause an error.
115+
A directive must have a `selector` property defined in order to match some element in a template.
116+
117+
## When do I need a `@Directive()` decorator without a selector?
118+
119+
If you're using dependency injection, or any Angular-specific
120+
feature, such as `@HostBinding()`, `@ViewChild()`, or `@Input()`, you need a
121+
`@Directive()` or `@Component()` decorator.
122+
The decorator lets the compiler know to generate the correct
123+
instructions to create that class and any classes that extend it.
124+
If you don't want to use that base class as a directive directly, leave
125+
the selector blank. If you do want it to be usable independently,
126+
fill in the metadata as usual.
127+
128+
Classes that don't use Angular features don't need an Angular decorator.
129+
130+
## I'm a library author. Should I add the `@Directive()` decorator to base classes?
131+
132+
133+
As support for selectorless decorators is introduced in
134+
Angular version 9, if you want to support Angular version 8 and earlier, you
135+
shouldn't add a selectorless `@Directive()` decorator.
136+
You can either add `@Directive()` with a selector or
137+
add an explicit constructor to affected subclasses.
138+

0 commit comments

Comments
 (0)