Skip to content

Commit c67a299

Browse files
authored
refactor(cdk/menu): use inject for all injection (#24941)
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 b66cf88 commit c67a299

File tree

14 files changed

+328
-369
lines changed

14 files changed

+328
-369
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: 13 additions & 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-
Directive,
11-
Inject,
12-
Injectable,
13-
Injector,
14-
Input,
15-
OnDestroy,
16-
Optional,
17-
ViewContainerRef,
18-
} from '@angular/core';
9+
import {Directive, inject, Injectable, InjectFlags, Input, OnDestroy} from '@angular/core';
1910
import {Directionality} from '@angular/cdk/bidi';
2011
import {
2112
FlexibleConnectedPositionStrategy,
@@ -78,6 +69,15 @@ export type ContextMenuCoordinates = {x: number; y: number};
7869
],
7970
})
8071
export class CdkContextMenuTrigger extends CdkMenuTriggerBase implements OnDestroy {
72+
/** The CDK overlay service. */
73+
private readonly _overlay = inject(Overlay);
74+
75+
/** The directionality of the page. */
76+
private readonly _directionality = inject(Directionality, InjectFlags.Optional);
77+
78+
/** The app's context menu tracking registry */
79+
private readonly _contextMenuTracker = inject(ContextMenuTracker);
80+
8181
/** Whether the context menu is disabled. */
8282
@Input('cdkContextMenuDisabled')
8383
get disabled(): boolean {
@@ -88,21 +88,8 @@ export class CdkContextMenuTrigger extends CdkMenuTriggerBase implements OnDestr
8888
}
8989
private _disabled = false;
9090

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);
91+
constructor() {
92+
super();
10693
this._setMenuStackCloseListener();
10794
}
10895

@@ -155,7 +142,7 @@ export class CdkContextMenuTrigger extends CdkMenuTriggerBase implements OnDestr
155142
return new OverlayConfig({
156143
positionStrategy: this._getOverlayPositionStrategy(coordinates),
157144
scrollStrategy: this._overlay.scrollStrategies.reposition(),
158-
direction: this._directionality,
145+
direction: this._directionality || undefined,
159146
});
160147
}
161148

src/cdk/menu/menu-aim.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
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 {Directive, inject, Injectable, InjectionToken, NgZone, OnDestroy} from '@angular/core';
1010
import {fromEvent, Subject} from 'rxjs';
11-
import {takeUntil, filter} from 'rxjs/operators';
12-
import {PointerFocusTracker, FocusableElement} from './pointer-focus-tracker';
11+
import {filter, takeUntil} from 'rxjs/operators';
12+
import {FocusableElement, PointerFocusTracker} from './pointer-focus-tracker';
1313
import {Menu} from './menu-interface';
14-
import {throwMissingPointerFocusTracker, throwMissingMenuReference} from './menu-errors';
14+
import {throwMissingMenuReference, throwMissingPointerFocusTracker} from './menu-errors';
1515

1616
/**
1717
* MenuAim is responsible for determining if a sibling menuitem's menu should be closed when a
@@ -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: 22 additions & 24 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';
@@ -28,7 +27,7 @@ import {mapTo, mergeAll, mergeMap, startWith, switchMap, takeUntil} from 'rxjs/o
2827
import {MENU_STACK, MenuStack, MenuStackItem} from './menu-stack';
2928
import {Menu} from './menu-interface';
3029
import {PointerFocusTracker} from './pointer-focus-tracker';
31-
import {MENU_AIM, MenuAim} from './menu-aim';
30+
import {MENU_AIM} from './menu-aim';
3231

3332
/** Counter used to create unique IDs for menus. */
3433
let nextId = 0;
@@ -54,6 +53,21 @@ 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 = inject(MENU_AIM, InjectFlags.Optional | InjectFlags.Self);
67+
68+
/** The directionality (text direction) of the current page. */
69+
protected readonly dir = inject(Directionality, InjectFlags.Optional);
70+
5771
/** The id of the menu's host element. */
5872
@Input() id = `cdk-menu-${nextId++}`;
5973

@@ -64,12 +78,12 @@ export abstract class CdkMenuBase
6478
/** The direction items in the menu flow. */
6579
orientation: 'horizontal' | 'vertical' = 'vertical';
6680

67-
/** Whether the menu is displayed inline (i.e. always present vs a conditional popup that the user triggers with a trigger element). */
81+
/**
82+
* Whether the menu is displayed inline (i.e. always present vs a conditional popup that the
83+
* user triggers with a trigger element).
84+
*/
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,22 +99,6 @@ 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-
) {
100-
super();
101-
this.nativeElement = elementRef.nativeElement;
102-
}
103-
104102
ngAfterContentInit() {
105103
if (!this.isInline) {
106104
this.menuStack.push(this);

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

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,9 @@
77
*/
88

99
import {UniqueSelectionDispatcher} from '@angular/cdk/collections';
10-
import {Directive, ElementRef, Inject, NgZone, OnDestroy, Optional, Self} from '@angular/core';
11-
import {Directionality} from '@angular/cdk/bidi';
10+
import {Directive, inject, OnDestroy} from '@angular/core';
1211
import {CdkMenuItemSelectable} from './menu-item-selectable';
1312
import {CdkMenuItem} from './menu-item';
14-
import {CdkMenuTrigger} from './menu-trigger';
15-
import {CDK_MENU, Menu} from './menu-interface';
16-
import {MENU_AIM, MenuAim} from './menu-aim';
17-
import {MENU_STACK, MenuStack} from './menu-stack';
1813

1914
/** Counter used to set a unique id and name for a selectable item */
2015
let nextId = 0;
@@ -37,33 +32,17 @@ let nextId = 0;
3732
],
3833
})
3934
export class CdkMenuItemRadio extends CdkMenuItemSelectable implements OnDestroy {
35+
/** The unique selection dispatcher for this radio's `CdkMenuGroup`. */
36+
private readonly _selectionDispatcher = inject(UniqueSelectionDispatcher);
37+
4038
/** An ID to identify this radio item to the `UniqueSelectionDisptcher`. */
4139
private _id = `${nextId++}`;
4240

4341
/** Function to unregister the selection dispatcher */
4442
private _removeDispatcherListener: () => void;
4543

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-
44+
constructor() {
45+
super();
6746
this._registerDispatcherListener();
6847
}
6948

0 commit comments

Comments
 (0)