Skip to content

Commit 70ddc36

Browse files
devversionandrewseguin
authored andcommitted
refactor: add missing abstract directive decorators (#19256)
In version 10 of Angular, undecorated base classes using Angular features (including the use of lifecycle hooks) are no longer supported.
1 parent 9abc8ea commit 70ddc36

File tree

5 files changed

+24
-11
lines changed

5 files changed

+24
-11
lines changed

src/cdk-experimental/column-resize/resizable.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import {
1010
AfterViewInit,
11+
Directive,
1112
ElementRef,
1213
Injector,
1314
NgZone,
@@ -38,6 +39,7 @@ const OVERLAY_ACTIVE_CLASS = 'cdk-resizable-overlay-thumb-active';
3839
* Base class for Resizable directives which are applied to column headers to make those columns
3940
* resizable.
4041
*/
42+
@Directive()
4143
export abstract class Resizable<HandleComponent extends ResizeOverlayHandle>
4244
implements AfterViewInit, OnDestroy {
4345
protected minWidthPxInternal: number = 0;

src/cdk/table/row.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export const CDK_ROW_TEMPLATE = `<ng-container cdkCellOutlet></ng-container>`;
3737
* Base class for the CdkHeaderRowDef and CdkRowDef that handles checking their columns inputs
3838
* for changes and notifying the table.
3939
*/
40+
@Directive()
4041
export abstract class BaseRowDef implements OnChanges {
4142
/** The columns to be displayed on this row. */
4243
columns: Iterable<string>;

src/material-experimental/mdc-list/list-base.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {AfterContentInit, ElementRef, NgZone, OnDestroy, QueryList} from '@angular/core';
9+
import {AfterContentInit, Directive, ElementRef, NgZone, OnDestroy, QueryList} from '@angular/core';
1010
import {setLines} from '@angular/material/core';
1111
import {Subscription} from 'rxjs';
1212
import {startWith} from 'rxjs/operators';
1313

1414
export class MatListBase {}
1515

16-
export class MatListItemBase implements AfterContentInit, OnDestroy {
16+
@Directive()
17+
export abstract class MatListItemBase implements AfterContentInit, OnDestroy {
1718
lines: QueryList<ElementRef<Element>>;
1819

1920
private _subscriptions = new Subscription();

tools/tslint-rules/noUndecoratedClassWithNgFieldsRule.ts renamed to tools/tslint-rules/noUndecoratedClassWithAngularFeaturesRule.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import * as Lint from 'tslint';
22
import * as ts from 'typescript';
33

4-
const RULE_FAILURE = `Undecorated class defines fields with Angular decorators. Undecorated ` +
5-
`classes with Angular fields cannot be extended in Ivy since no definition is generated. ` +
6-
`Add a "@Directive" decorator to fix this.`;
4+
const RULE_FAILURE = `Undecorated class uses Angular features. Undecorated ` +
5+
`classes using Angular features cannot be extended in Ivy since no definition is generated. ` +
6+
`Add an Angular decorator to fix this.`;
7+
8+
/** Set of lifecycle hooks that indicate that a given class declaration uses Angular features. */
9+
const LIFECYCLE_HOOKS = new Set([
10+
'ngOnChanges', 'ngOnInit', 'ngOnDestroy', 'ngDoCheck', 'ngAfterViewInit', 'ngAfterViewChecked',
11+
'ngAfterContentInit', 'ngAfterContentChecked'
12+
]);
713

814
/**
9-
* Rule that doesn't allow undecorated class declarations with fields using Angular
10-
* decorators.
15+
* Rule that doesn't allow undecorated class declarations using Angular features.
1116
*/
1217
export class Rule extends Lint.Rules.TypedRule {
1318
applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
@@ -28,7 +33,12 @@ class Walker extends Lint.RuleWalker {
2833
}
2934

3035
for (let member of node.members) {
31-
if (member.decorators && this._hasAngularDecorator(member)) {
36+
const hasLifecycleHook = member.name !== undefined &&
37+
ts.isIdentifier(member.name) && LIFECYCLE_HOOKS.has(member.name.text);
38+
// A class is considering using Angular features if it declares any of
39+
// the known Angular lifecycle hooks, or if it has class members that are
40+
// decorated with Angular decorators (e.g. `@Input`).
41+
if (hasLifecycleHook || this._hasAngularDecorator(member)) {
3242
this.addFailureAtNode(node, RULE_FAILURE);
3343
return;
3444
}
@@ -38,8 +48,7 @@ class Walker extends Lint.RuleWalker {
3848
/** Checks if the specified node has an Angular decorator. */
3949
private _hasAngularDecorator(node: ts.Node): boolean {
4050
return !!node.decorators && node.decorators.some(d => {
41-
if (!ts.isCallExpression(d.expression) ||
42-
!ts.isIdentifier(d.expression.expression)) {
51+
if (!ts.isCallExpression(d.expression) || !ts.isIdentifier(d.expression.expression)) {
4352
return false;
4453
}
4554

tslint.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@
112112
"no-import-export-spacing": true,
113113
"no-private-getters": true,
114114
"no-undecorated-base-class-di": true,
115-
"no-undecorated-class-with-ng-fields": true,
115+
"no-undecorated-class-with-angular-features": true,
116116
"setters-after-getters": true,
117117
"ng-on-changes-property-access": true,
118118
"require-breaking-change-version": true,

0 commit comments

Comments
 (0)