Skip to content

Commit 7e0db04

Browse files
committed
refactor(cdk/menu): use inject for all injection
This change switches the cdk menu from using constructor injection to using the `inject` function. This was enabled by angular/angular#45991. This approach has a number of advantages: * We can add and remove injectables without changing the public constructor signature * It reduces the boilerplate of passing arguments through `super` * It avoids long/messy constructor blocks in favor of simpler member definitions. * This sidesteps a common pitfall of making `@Optional` injection (`null` if not provided) as an optional parameter (`undefined` if not provided)`.
1 parent b2e6d7e commit 7e0db04

File tree

11 files changed

+334
-269
lines changed

11 files changed

+334
-269
lines changed

package.json

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@
5454
},
5555
"version": "14.1.0-next.0",
5656
"dependencies": {
57-
"@angular/animations": "14.0.0-rc.0",
58-
"@angular/common": "14.0.0-rc.0",
59-
"@angular/compiler": "14.0.0-rc.0",
60-
"@angular/core": "14.0.0-rc.0",
61-
"@angular/forms": "14.0.0-rc.0",
62-
"@angular/platform-browser": "14.0.0-rc.0",
57+
"@angular/animations": "14.0.0-rc.1",
58+
"@angular/common": "14.0.0-rc.1",
59+
"@angular/compiler": "14.0.0-rc.1",
60+
"@angular/core": "14.0.0-rc.1",
61+
"@angular/forms": "14.0.0-rc.1",
62+
"@angular/platform-browser": "14.0.0-rc.1",
6363
"@types/google.maps": "^3.47.3",
6464
"@types/youtube": "^0.0.46",
6565
"rxjs": "^6.6.7",
@@ -68,17 +68,17 @@
6868
"zone.js": "~0.11.5"
6969
},
7070
"devDependencies": {
71-
"@angular-devkit/build-angular": "14.0.0-rc.0",
72-
"@angular-devkit/core": "14.0.0-rc.0",
73-
"@angular-devkit/schematics": "14.0.0-rc.0",
74-
"@angular/bazel": "14.0.0-rc.0",
75-
"@angular/cli": "14.0.0-rc.0",
76-
"@angular/compiler-cli": "14.0.0-rc.0",
71+
"@angular-devkit/build-angular": "14.0.0-rc.1",
72+
"@angular-devkit/core": "14.0.0-rc.1",
73+
"@angular-devkit/schematics": "14.0.0-rc.1",
74+
"@angular/bazel": "14.0.0-rc.1",
75+
"@angular/cli": "14.0.0-rc.1",
76+
"@angular/compiler-cli": "14.0.0-rc.1",
7777
"@angular/dev-infra-private": "https://github.com/angular/dev-infra-private-builds.git#ce1d7f6c62ff0f9569791d78920c3628c56b4574",
78-
"@angular/localize": "14.0.0-rc.0",
79-
"@angular/platform-browser-dynamic": "14.0.0-rc.0",
80-
"@angular/platform-server": "14.0.0-rc.0",
81-
"@angular/router": "14.0.0-rc.0",
78+
"@angular/localize": "14.0.0-rc.1",
79+
"@angular/platform-browser-dynamic": "14.0.0-rc.1",
80+
"@angular/platform-server": "14.0.0-rc.1",
81+
"@angular/router": "14.0.0-rc.1",
8282
"@axe-core/webdriverjs": "^4.3.2",
8383
"@babel/core": "^7.16.12",
8484
"@bazel/bazelisk": "~1.11.0",
@@ -143,7 +143,7 @@
143143
"@octokit/rest": "18.3.5",
144144
"@rollup/plugin-commonjs": "^21.0.0",
145145
"@rollup/plugin-node-resolve": "^13.1.3",
146-
"@schematics/angular": "14.0.0-rc.0",
146+
"@schematics/angular": "14.0.0-rc.1",
147147
"@types/babel__core": "^7.1.18",
148148
"@types/browser-sync": "^2.26.3",
149149
"@types/fs-extra": "^9.0.13",

src/cdk/menu/context-menu-trigger.ts

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88

99
import {
1010
Directive,
11+
inject,
1112
Inject,
1213
Injectable,
14+
InjectFlags,
1315
Injector,
1416
Input,
1517
OnDestroy,
@@ -78,6 +80,18 @@ export type ContextMenuCoordinates = {x: number; y: number};
7880
],
7981
})
8082
export class CdkContextMenuTrigger extends CdkMenuTriggerBase implements OnDestroy {
83+
/** The CDK overlay service. */
84+
private readonly _overlay = inject(Overlay);
85+
86+
/** The directionality of the page. */
87+
private readonly _directionality: Directionality | null = inject(
88+
Directionality,
89+
InjectFlags.Optional,
90+
);
91+
92+
/** The app's context menu tracking registry */
93+
private readonly _contextMenuTracker = inject(ContextMenuTracker);
94+
8195
/** Whether the context menu is disabled. */
8296
@Input('cdkContextMenuDisabled')
8397
get disabled(): boolean {
@@ -88,21 +102,8 @@ export class CdkContextMenuTrigger extends CdkMenuTriggerBase implements OnDestr
88102
}
89103
private _disabled = false;
90104

91-
constructor(
92-
/** The DI injector for this component */
93-
injector: Injector,
94-
/** The view container ref for this component */
95-
viewContainerRef: ViewContainerRef,
96-
/** The CDK overlay service */
97-
private readonly _overlay: Overlay,
98-
/** The app's context menu tracking registry */
99-
private readonly _contextMenuTracker: ContextMenuTracker,
100-
/** The menu stack this menu is part of. */
101-
@Inject(MENU_STACK) menuStack: MenuStack,
102-
/** The directionality of the current page */
103-
@Optional() private readonly _directionality?: Directionality,
104-
) {
105-
super(injector, viewContainerRef, menuStack);
105+
constructor() {
106+
super();
106107
this._setMenuStackCloseListener();
107108
}
108109

@@ -116,7 +117,7 @@ export class CdkContextMenuTrigger extends CdkMenuTriggerBase implements OnDestr
116117

117118
/** Close the currently opened context menu. */
118119
close() {
119-
this.menuStack.closeAll();
120+
this.menuStack?.closeAll();
120121
}
121122

122123
/**
@@ -155,7 +156,7 @@ export class CdkContextMenuTrigger extends CdkMenuTriggerBase implements OnDestr
155156
return new OverlayConfig({
156157
positionStrategy: this._getOverlayPositionStrategy(coordinates),
157158
scrollStrategy: this._overlay.scrollStrategies.reposition(),
158-
direction: this._directionality,
159+
direction: this._directionality || undefined,
159160
});
160161
}
161162

@@ -176,7 +177,7 @@ export class CdkContextMenuTrigger extends CdkMenuTriggerBase implements OnDestr
176177

177178
/** Subscribe to the menu stack close events and close this menu when requested. */
178179
private _setMenuStackCloseListener() {
179-
this.menuStack.closed.pipe(takeUntil(this.destroyed)).subscribe(({item}) => {
180+
this.menuStack?.closed.pipe(takeUntil(this.destroyed)).subscribe(({item}) => {
180181
if (item === this.childMenu && this.isOpen()) {
181182
this.closed.next();
182183
this.overlayRef!.detach();
@@ -200,7 +201,7 @@ export class CdkContextMenuTrigger extends CdkMenuTriggerBase implements OnDestr
200201
}
201202
outsideClicks.pipe(takeUntil(this.stopOutsideClicksListener)).subscribe(event => {
202203
if (!this.isElementInsideMenuStack(event.target as Element)) {
203-
this.menuStack.closeAll();
204+
this.menuStack?.closeAll();
204205
}
205206
});
206207
}
@@ -218,7 +219,7 @@ export class CdkContextMenuTrigger extends CdkMenuTriggerBase implements OnDestr
218219
if (this.isOpen()) {
219220
// since we're moving this menu we need to close any submenus first otherwise they end up
220221
// disconnected from this one.
221-
this.menuStack.closeSubMenuOf(this.childMenu!);
222+
this.menuStack?.closeSubMenuOf(this.childMenu!);
222223

223224
(
224225
this.overlayRef!.getConfig().positionStrategy as FlexibleConnectedPositionStrategy

src/cdk/menu/menu-aim.ts

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

9-
import {Injectable, NgZone, OnDestroy, InjectionToken, Directive} from '@angular/core';
9+
import {Injectable, NgZone, OnDestroy, InjectionToken, Directive, inject} from '@angular/core';
1010
import {fromEvent, Subject} from 'rxjs';
1111
import {takeUntil, filter} from 'rxjs/operators';
1212
import {PointerFocusTracker, FocusableElement} from './pointer-focus-tracker';
@@ -102,6 +102,9 @@ function isWithinSubmenu(submenuPoints: DOMRect, m: number, b: number) {
102102
*/
103103
@Injectable()
104104
export class TargetMenuAim implements MenuAim, OnDestroy {
105+
/** The Angular zone. */
106+
private readonly _ngZone = inject(NgZone);
107+
105108
/** The last NUM_POINTS mouse move events. */
106109
private readonly _points: Point[] = [];
107110

@@ -117,11 +120,6 @@ export class TargetMenuAim implements MenuAim, OnDestroy {
117120
/** Emits when this service is destroyed. */
118121
private readonly _destroyed: Subject<void> = new Subject();
119122

120-
constructor(
121-
/** The Angular zone. */
122-
private readonly _ngZone: NgZone,
123-
) {}
124-
125123
ngOnDestroy() {
126124
this._destroyed.next();
127125
this._destroyed.complete();

src/cdk/menu/menu-bar.ts

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {
10-
AfterContentInit,
11-
Directive,
12-
ElementRef,
13-
Inject,
14-
NgZone,
15-
Optional,
16-
Self,
17-
} from '@angular/core';
18-
import {Directionality} from '@angular/cdk/bidi';
9+
import {AfterContentInit, Directive} from '@angular/core';
1910
import {
2011
DOWN_ARROW,
2112
ESCAPE,
@@ -29,7 +20,6 @@ import {takeUntil} from 'rxjs/operators';
2920
import {CdkMenuGroup} from './menu-group';
3021
import {CDK_MENU} from './menu-interface';
3122
import {FocusNext, MENU_STACK, MenuStack} from './menu-stack';
32-
import {MENU_AIM, MenuAim} from './menu-aim';
3323
import {CdkMenuBase} from './menu-base';
3424

3525
/**
@@ -59,21 +49,6 @@ export class CdkMenuBar extends CdkMenuBase implements AfterContentInit {
5949
/** Whether the menu is displayed inline (i.e. always present vs a conditional popup that the user triggers with a trigger element). */
6050
override readonly isInline = true;
6151

62-
constructor(
63-
/** The host element. */
64-
elementRef: ElementRef<HTMLElement>,
65-
/** The Angular zone. */
66-
ngZone: NgZone,
67-
/** The menu stack this menu is part of. */
68-
@Inject(MENU_STACK) menuStack: MenuStack,
69-
/** The menu aim service used by this menu. */
70-
@Self() @Optional() @Inject(MENU_AIM) menuAim?: MenuAim,
71-
/** The directionality of the page. */
72-
@Optional() dir?: Directionality,
73-
) {
74-
super(elementRef, ngZone, menuStack, menuAim, dir);
75-
}
76-
7752
override ngAfterContentInit() {
7853
super.ngAfterContentInit();
7954
this._subscribeToMenuStackEmptied();

src/cdk/menu/menu-base.ts

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,12 @@ import {
1212
ContentChildren,
1313
Directive,
1414
ElementRef,
15-
Inject,
15+
inject,
16+
InjectFlags,
1617
Input,
1718
NgZone,
1819
OnDestroy,
19-
Optional,
2020
QueryList,
21-
Self,
2221
} from '@angular/core';
2322
import {FocusKeyManager, FocusOrigin} from '@angular/cdk/a11y';
2423
import {CdkMenuItem} from './menu-item';
@@ -54,6 +53,24 @@ export abstract class CdkMenuBase
5453
extends CdkMenuGroup
5554
implements Menu, AfterContentInit, OnDestroy
5655
{
56+
/** The menu's native DOM host element. */
57+
readonly nativeElement: HTMLElement = inject(ElementRef).nativeElement;
58+
59+
/** The Angular zone. */
60+
protected ngZone = inject(NgZone);
61+
62+
/** The stack of menus this menu belongs to. */
63+
readonly menuStack: MenuStack = inject(MENU_STACK);
64+
65+
/** The menu aim service used by this menu. */
66+
protected readonly menuAim: MenuAim | null = inject(
67+
MENU_AIM,
68+
InjectFlags.Optional | InjectFlags.Self,
69+
);
70+
71+
/** The directionality (text direction) of the current page. */
72+
protected readonly dir: Directionality | null = inject(Directionality, InjectFlags.Optional);
73+
5774
/** The id of the menu's host element. */
5875
@Input() id = `cdk-menu-${nextId++}`;
5976

@@ -67,9 +84,6 @@ export abstract class CdkMenuBase
6784
/** Whether the menu is displayed inline (i.e. always present vs a conditional popup that the user triggers with a trigger element). */
6885
isInline = false;
6986

70-
/** The menu's native DOM host element. */
71-
readonly nativeElement: HTMLElement;
72-
7387
/** Handles keyboard events for the menu. */
7488
protected keyManager: FocusKeyManager<CdkMenuItem>;
7589

@@ -85,20 +99,8 @@ export abstract class CdkMenuBase
8599
/** Whether this menu's menu stack has focus. */
86100
private _menuStackHasFocus = false;
87101

88-
protected constructor(
89-
/** The host element. */
90-
elementRef: ElementRef<HTMLElement>,
91-
/** The Angular zone. */
92-
protected ngZone: NgZone,
93-
/** The stack of menus this menu belongs to. */
94-
@Inject(MENU_STACK) readonly menuStack: MenuStack,
95-
/** The menu aim service used by this menu. */
96-
@Self() @Optional() @Inject(MENU_AIM) protected readonly menuAim?: MenuAim,
97-
/** The directionality of the current page. */
98-
@Optional() protected readonly dir?: Directionality,
99-
) {
102+
protected constructor() {
100103
super();
101-
this.nativeElement = elementRef.nativeElement;
102104
}
103105

104106
ngAfterContentInit() {

src/cdk/menu/menu-item-radio.ts

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,16 @@
77
*/
88

99
import {UniqueSelectionDispatcher} from '@angular/cdk/collections';
10-
import {Directive, ElementRef, Inject, NgZone, OnDestroy, Optional, Self} from '@angular/core';
10+
import {
11+
Directive,
12+
ElementRef,
13+
inject,
14+
Inject,
15+
NgZone,
16+
OnDestroy,
17+
Optional,
18+
Self,
19+
} from '@angular/core';
1120
import {Directionality} from '@angular/cdk/bidi';
1221
import {CdkMenuItemSelectable} from './menu-item-selectable';
1322
import {CdkMenuItem} from './menu-item';
@@ -37,33 +46,17 @@ let nextId = 0;
3746
],
3847
})
3948
export class CdkMenuItemRadio extends CdkMenuItemSelectable implements OnDestroy {
49+
/** The unique selection dispatcher for this radio's `CdkMenuGroup`. */
50+
private readonly _selectionDispatcher = inject(UniqueSelectionDispatcher);
51+
4052
/** An ID to identify this radio item to the `UniqueSelectionDisptcher`. */
4153
private _id = `${nextId++}`;
4254

4355
/** Function to unregister the selection dispatcher */
4456
private _removeDispatcherListener: () => void;
4557

46-
constructor(
47-
/** The host element for this radio item. */
48-
element: ElementRef<HTMLElement>,
49-
/** The Angular zone. */
50-
ngZone: NgZone,
51-
/** The unique selection dispatcher for this radio's `CdkMenuGroup`. */
52-
private readonly _selectionDispatcher: UniqueSelectionDispatcher,
53-
/** The menu stack this item belongs to. */
54-
@Inject(MENU_STACK) menuStack: MenuStack,
55-
/** The parent menu for this item. */
56-
@Optional() @Inject(CDK_MENU) parentMenu?: Menu,
57-
/** The menu aim used for this item. */
58-
@Optional() @Inject(MENU_AIM) menuAim?: MenuAim,
59-
/** The directionality of the page. */
60-
@Optional() dir?: Directionality,
61-
/** Reference to the CdkMenuItemTrigger directive if one is added to the same element */
62-
// tslint:disable-next-line: lightweight-tokens
63-
@Self() @Optional() menuTrigger?: CdkMenuTrigger,
64-
) {
65-
super(element, ngZone, menuStack, parentMenu, menuAim, dir, menuTrigger);
66-
58+
constructor() {
59+
super();
6760
this._registerDispatcherListener();
6861
}
6962

0 commit comments

Comments
 (0)