Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: annotate remaining base classes with @directive for ivy #17279

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/cdk-experimental/popover-edit/popover-edit.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {LEFT_ARROW, UP_ARROW, RIGHT_ARROW, DOWN_ARROW, TAB} from '@angular/cdk/k
import {CdkTableModule} from '@angular/cdk/table';
import {dispatchKeyboardEvent} from '@angular/cdk/testing';
import {CommonModule} from '@angular/common';
import {Component, ElementRef, Type, ViewChild} from '@angular/core';
import {Component, Directive, ElementRef, Type, ViewChild} from '@angular/core';
import {ComponentFixture, fakeAsync, flush, TestBed, tick, inject} from '@angular/core/testing';
import {FormsModule, NgForm} from '@angular/forms';
import {BidiModule, Direction} from '@angular/cdk/bidi';
Expand Down Expand Up @@ -61,6 +61,10 @@ interface PeriodicElement {
weight: number;
}

@Directive({
// TODO(devversion): this selector can be removed when we update to Angular 9.0.
selector: 'do-not-use-abstract-cdk-popover-edit-base-test-component'
})
abstract class BaseTestComponent {
@ViewChild('table', {static: false}) table: ElementRef;

Expand Down
18 changes: 15 additions & 3 deletions src/material-experimental/mdc-button/button-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import {Platform} from '@angular/cdk/platform';
import {ElementRef, NgZone, ViewChild} from '@angular/core';
import {Directive, ElementRef, Inject, NgZone, Optional, ViewChild} from '@angular/core';
import {
CanColor,
CanColorCtor,
Expand All @@ -21,6 +21,7 @@ import {
mixinDisableRipple,
RippleAnimationConfig
} from '@angular/material/core';
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';
import {numbers} from '@material/ripple';

/** Inputs common to all buttons. */
Expand Down Expand Up @@ -78,6 +79,10 @@ export const _MatButtonBaseMixin: CanDisableRippleCtor&CanDisableCtor&CanColorCt
typeof MatButtonMixinCore = mixinColor(mixinDisabled(mixinDisableRipple(MatButtonMixinCore)));

/** Base class for all buttons. */
@Directive({
// TODO(devversion): this selector can be removed when we update to Angular 9.0.
selector: 'do-not-use-abstract-mat-button-base'
Splaktar marked this conversation as resolved.
Show resolved Hide resolved
})
export class MatButtonBase extends _MatButtonBaseMixin implements CanDisable, CanColor,
CanDisableRipple {
/** The ripple animation configuration to use for the buttons. */
Expand All @@ -94,7 +99,8 @@ export class MatButtonBase extends _MatButtonBaseMixin implements CanDisable, Ca

constructor(
elementRef: ElementRef, public _platform: Platform, public _ngZone: NgZone,
public _animationMode?: string) {
// TODO(devversion): Injection can be removed if angular/angular#32981 is fixed.
@Optional() @Inject(ANIMATION_MODULE_TYPE) public _animationMode?: string) {
super(elementRef);

// For each of the variant selectors that is present in the button's host
Expand Down Expand Up @@ -144,10 +150,16 @@ export const MAT_ANCHOR_HOST = {
/**
* Anchor button base.
*/
@Directive({
// TODO(devversion): this selector can be removed when we update to Angular 9.0.
selector: 'do-not-use-abstract-mat-anchor-base'
})
export class MatAnchorBase extends MatButtonBase {
tabIndex: number;

constructor(elementRef: ElementRef, platform: Platform, ngZone: NgZone, animationMode?: string) {
constructor(elementRef: ElementRef, platform: Platform, ngZone: NgZone,
// TODO(devversion): Injection can be removed if angular/angular#32981 is fixed.
@Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string) {
super(elementRef, platform, ngZone, animationMode);
}

Expand Down
5 changes: 5 additions & 0 deletions src/material-experimental/mdc-button/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
import {MatCommonModule, MatRippleModule} from '@angular/material/core';
import {MatAnchor, MatButton} from './button';
import {MatAnchorBase, MatButtonBase} from './button-base';
import {MatFabAnchor, MatFabButton} from './fab';
import {MatIconAnchor, MatIconButton} from './icon-button';

Expand All @@ -31,6 +32,10 @@ import {MatIconAnchor, MatIconButton} from './icon-button';
MatIconButton,
MatFabAnchor,
MatFabButton,
// TODO(devversion): remove when `MatButtonBase` becomes a selectorless Directive.
MatButtonBase,
// TODO(devversion): remove when `MatAnchorBase` becomes a selectorless Directive.
MatAnchorBase,
],
})
export class MatButtonModule {
Expand Down
6 changes: 5 additions & 1 deletion src/material-experimental/popover-edit/popover-edit.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {LEFT_ARROW, UP_ARROW, RIGHT_ARROW, DOWN_ARROW, TAB} from '@angular/cdk/k
import {MatTableModule} from '@angular/material/table';
import {dispatchKeyboardEvent} from '@angular/cdk/testing';
import {CommonModule} from '@angular/common';
import {Component, ElementRef, Type, ViewChild} from '@angular/core';
import {Component, Directive, ElementRef, Type, ViewChild} from '@angular/core';
import {ComponentFixture, fakeAsync, flush, TestBed, tick, inject} from '@angular/core/testing';
import {FormsModule, NgForm} from '@angular/forms';
import {OverlayContainer} from '@angular/cdk/overlay';
Expand Down Expand Up @@ -59,6 +59,10 @@ interface PeriodicElement {
weight: number;
}

@Directive({
// TODO(devversion): this selector can be removed when we update to Angular 9.0.
selector: 'do-not-use-abstract-mat-popover-edit-base-test-component'
})
abstract class BaseTestComponent {
@ViewChild('table', {static: false}) table: ElementRef;

Expand Down
11 changes: 9 additions & 2 deletions src/material/menu/menu-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
import {MatCommonModule, MatRippleModule} from '@angular/material/core';
import {MatMenuContent} from './menu-content';
import {_MatMenu} from './menu';
import {_MatMenu, _MatMenuBase, MatMenu} from './menu';
import {MatMenuItem} from './menu-item';
import {
MatMenuTrigger,
Expand All @@ -24,7 +24,14 @@ import {
*/
@NgModule({
exports: [MatMenuTrigger, MatMenuContent, MatCommonModule],
declarations: [MatMenuTrigger, MatMenuContent],
declarations: [
MatMenuTrigger,
MatMenuContent,
// TODO(devversion): remove when `MatMenu` becomes a selectorless Directive.
MatMenu,
// TODO(devversion): remove when `_MatMenuBase` becomes a selectorless Directive.
_MatMenuBase
],
providers: [MAT_MENU_SCROLL_STRATEGY_FACTORY_PROVIDER]
})
// tslint:disable-next-line:class-name
Expand Down
9 changes: 9 additions & 0 deletions src/material/menu/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
Component,
ContentChild,
ContentChildren,
Directive,
ElementRef,
EventEmitter,
Inject,
Expand Down Expand Up @@ -90,6 +91,10 @@ export function MAT_MENU_DEFAULT_OPTIONS_FACTORY(): MatMenuDefaultOptions {
const MAT_MENU_BASE_ELEVATION = 4;

/** Base class with all of the `MatMenu` functionality. */
@Directive({
// TODO(devversion): this selector can be removed when we update to Angular 9.0.
selector: 'do-not-use-abstract-mat-menu-base'
})
// tslint:disable-next-line:class-name
export class _MatMenuBase implements AfterContentInit, MatMenuPanel<MatMenuItem>, OnInit,
OnDestroy {
Expand Down Expand Up @@ -445,6 +450,10 @@ export class _MatMenuBase implements AfterContentInit, MatMenuPanel<MatMenuItem>
}

/** @docs-private We show the "_MatMenu" class as "MatMenu" in the docs. */
@Directive({
// TODO(devversion): this selector can be removed when we update to Angular 9.0.
selector: 'do-not-use-abstract-mat-menu'
})
export class MatMenu extends _MatMenuBase {}

// Note on the weird inheritance setup: we need three classes, because the MDC-based menu has to
Expand Down
65 changes: 65 additions & 0 deletions tools/tslint-rules/noUndecoratedClassWithNgFieldsRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import * as Lint from 'tslint';
import * as ts from 'typescript';

const RULE_FAILURE = `Undecorated class defines fields with Angular decorators. Undecorated ` +
`classes with Angular fields cannot be extended in Ivy since no definition is generated. ` +
`Add a "@Directive" decorator to fix this.`;

/**
* Rule that doesn't allow undecorated class declarations with fields using Angular
* decorators.
*/
export class Rule extends Lint.Rules.TypedRule {
applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
return this.applyWithWalker(
new Walker(sourceFile, this.getOptions(), program.getTypeChecker()));
}
}

class Walker extends Lint.RuleWalker {
constructor(
sourceFile: ts.SourceFile, options: Lint.IOptions, private _typeChecker: ts.TypeChecker) {
super(sourceFile, options);
}

visitClassDeclaration(node: ts.ClassDeclaration) {
if (this._hasAngularDecorator(node)) {
return;
}

for (let member of node.members) {
if (member.decorators && this._hasAngularDecorator(member)) {
this.addFailureAtNode(node, RULE_FAILURE);
return;
}
}
}

/** Checks if the specified node has an Angular decorator. */
private _hasAngularDecorator(node: ts.Node): boolean {
return !!node.decorators && node.decorators.some(d => {
if (!ts.isCallExpression(d.expression) ||
!ts.isIdentifier(d.expression.expression)) {
return false;
}

const moduleImport = this._getModuleImportOfIdentifier(d.expression.expression);
return moduleImport ? moduleImport.startsWith('@angular/core') : false;
});
}

/** Gets the module import of the given identifier if imported. */
private _getModuleImportOfIdentifier(node: ts.Identifier): string|null {
const symbol = this._typeChecker.getSymbolAtLocation(node);
if (!symbol || !symbol.declarations.length) {
return null;
}
const decl = symbol.declarations[0];
if (!ts.isImportSpecifier(decl)) {
return null;
}
const importDecl = decl.parent.parent.parent;
devversion marked this conversation as resolved.
Show resolved Hide resolved
const moduleSpecifier = importDecl.moduleSpecifier;
return ts.isStringLiteral(moduleSpecifier) ? moduleSpecifier.text : null;
}
}
1 change: 1 addition & 0 deletions tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
"no-import-spacing": true,
"no-private-getters": true,
"no-undecorated-base-class-di": true,
"no-undecorated-class-with-ng-fields": true,
"setters-after-getters": true,
"ng-on-changes-property-access": true,
"rxjs-imports": true,
Expand Down