From 32b042014df9c108145855523a0b259c56383927 Mon Sep 17 00:00:00 2001 From: Kapunahele Wong Date: Wed, 9 Oct 2019 16:31:25 -0400 Subject: [PATCH] docs: add field decorators to undecorated classes migration (#33069) PR Close #33069 --- .../guide/migration-undecorated-classes.md | 110 +++++++++++++++--- 1 file changed, 94 insertions(+), 16 deletions(-) diff --git a/aio/content/guide/migration-undecorated-classes.md b/aio/content/guide/migration-undecorated-classes.md index dcbf40ffad..4b7d5ce4cd 100644 --- a/aio/content/guide/migration-undecorated-classes.md +++ b/aio/content/guide/migration-undecorated-classes.md @@ -1,14 +1,17 @@ -# Undecorated classes migration (DI) - -This section discusses an Angular version 9 schematic that migrates -two inheritance patterns that need to be updated to work with Ivy. +# Undecorated Classes Migration ## What does this migration do? This migration adds an empty `@Directive()` decorator to undecorated -base classes that are extended by either directives or components. +base classes that: - Before: +- use Angular features +- are extended by directives or components + +For example, in the diff below, a `@Directive()` decorator is added to `BaseMenu` because `BaseMenu` uses dependency injection. + + + **Before:** ```ts export class BaseMenu { constructor(private vcr: ViewContainerRef) {} @@ -18,7 +21,7 @@ base classes that are extended by either directives or components. export class SettingsMenu extends BaseMenu {} ``` - After: + **After:** ```ts @Directive() export class BaseMenu { @@ -29,9 +32,9 @@ base classes that are extended by either directives or components. export class SettingsMenu extends BaseMenu {} ``` -The schematic also copies any inherited directive or component metadata to the derived class. +In the event that a directive or component is extended by a class without a decorator, the schematic copies any inherited directive or component metadata to the derived class. -Before: +**Before:** ```ts @Component({ selector: 'base-menu', @@ -42,7 +45,7 @@ class BaseMenu {} export class SettingsMenu extends BaseMenu {} ``` -After: +**After:** ```ts @Component({ selector: 'base-menu', @@ -51,14 +54,55 @@ After: class BaseMenu {} @Component({ - selector: 'settings-menu', + selector: 'base-menu', template: '
' }) export class SettingsMenu extends BaseMenu {} ``` +This schematic also decorates classes that use Angular field decorators, including: +- `@Input()` +- `@Output()` +- `@HostBinding()` +- `@HostListener()` +- `@ViewChild()` / `@ViewChildren()` +- `@ContentChild()` / `@ContentChildren()` + + +**Before:** +```ts +class Base { + @Output() + countChanged = new EventEmitter(); +} + +@Directive({ + selector: '[myDir]' +}) +class Dir extends Base { +} +``` + +**After:** +```ts +@Directive() // schematic adds @Directive() +class Base { + @Output() + countChanged = new EventEmitter(); +} + +@Directive({ + selector: '[myDir]' +}) +class Dir extends Base { +} +``` + + ## Why is this migration necessary? +### Migrating classes that use DI + When a class has a `@Directive()` or `@Component()` decorator, the Angular compiler generates extra code to inject dependencies into the constructor. When using inheritance, Ivy needs both the parent class @@ -66,11 +110,11 @@ and the child class to apply a decorator to generate the correct code. You can think of this change as two cases: a parent class is missing a decorator or a child class is missing a decorator. In both scenarios, -Angular's run-time needs additional information from the compiler. +Angular's runtime needs additional information from the compiler. This additional information comes from adding decorators. -### Decorator missing from parent class +#### Decorator missing from parent class When the decorator is missing from the parent class, the subclass will inherit a constructor from a class for @@ -91,7 +135,7 @@ provides this information. In the future, add `@Directive()` to base classes that do not already have decorators and are extended by directives. -### Decorator missing from child class +#### Decorator missing from child class When the child class is missing the decorator, the child class inherits from the @@ -101,6 +145,37 @@ that the class is a `@Directive` or `@Component`, so it doesn't generate the proper instructions for the directive. +### Migrating classes that use field decorators + +In ViewEngine, base classes with field decorators like `@Input()` worked +even when the class did not have a `@Directive()` or `@Component()` decorator. +For example: + +```ts +class Base { + @Input() + foo: string; +} + +@Directive(...) +class Dir extends Base { + ngOnChanges(): void { + // notified when bindings to [foo] are updated + } +} +``` + +However, this example won't compile with Ivy because the `Base` class +_requires_ either a `@Directive()` or `@Component()` decorator to generate +code for inputs, outputs, queries, and host bindings. + +Always requiring a class decorator leads to two main benefits for Angular: + +1. The previous behavior was inconsistent. Some Angular features required a decorator (dependency injection), but others did not. Now, all Angular features consistently require a class decorator. + +1. Supporting undecorated classes increases the code size and complexity of Angular. Always requiring class decorators allows the framework to become smaller and simpler for all users. + + ## What does it mean to have a `@Directive()` decorator with no metadata inside of it? The presence of the `@Directive` decorator causes Angular to generate @@ -129,10 +204,13 @@ Classes that don't use Angular features don't need an Angular decorator. ## I'm a library author. Should I add the `@Directive()` decorator to base classes? - As support for selectorless decorators is introduced in Angular version 9, if you want to support Angular version 8 and earlier, you shouldn't add a selectorless `@Directive()` decorator. You can either add `@Directive()` with a selector or -add an explicit constructor to affected subclasses. +move the Angular-specific features to affected subclasses. + +## What about applications using non-migrated libraries? + +`ngcc` should transform any non-migrated libraries to generate the proper code.