Skip to content

Commit 5412fb1

Browse files
authored
refactor(cdk-experimental/menu): move listeners from host object to host listener decorator (#20164)
Some of the Cdk directives are expected to be extended by some subclasses (e.g. material). In Ivy the host metadata will be merged whereas in ViewEngine it will be overridden forcing duplication of listeners in the host object on the subclass which results in double listeners in Ivy. Moving to @HostListener prevents having to duplicate them in subclasses.
1 parent 0398d93 commit 5412fb1

File tree

4 files changed

+43
-8
lines changed

4 files changed

+43
-8
lines changed

src/cdk-experimental/menu/menu-bar.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
OnDestroy,
1616
Optional,
1717
NgZone,
18+
HostListener,
1819
} from '@angular/core';
1920
import {Directionality} from '@angular/cdk/bidi';
2021
import {FocusKeyManager, FocusOrigin} from '@angular/cdk/a11y';
@@ -46,9 +47,6 @@ function isMenuElement(target: Element) {
4647
selector: '[cdkMenuBar]',
4748
exportAs: 'cdkMenuBar',
4849
host: {
49-
'(keydown)': '_handleKeyEvent($event)',
50-
'(document:click)': '_closeOnBackgroundClick($event)',
51-
'(focus)': 'focusFirstItem()',
5250
'role': 'menubar',
5351
'class': 'cdk-menu-bar',
5452
'tabindex': '0',
@@ -100,6 +98,11 @@ export class CdkMenuBar extends CdkMenuGroup implements Menu, AfterContentInit,
10098
this._subscribeToMouseManager();
10199
}
102100

101+
// In Ivy the `host` metadata will be merged, whereas in ViewEngine it is overridden. In order
102+
// to avoid double event listeners, we need to use `HostListener`. Once Ivy is the default, we
103+
// can move this back into `host`.
104+
// tslint:disable:no-host-decorator-in-concrete
105+
@HostListener('focus')
103106
/** Place focus on the first MenuItem in the menu and set the focus origin. */
104107
focusFirstItem(focusOrigin: FocusOrigin = 'program') {
105108
this._keyManager.setFocusOrigin(focusOrigin);
@@ -112,6 +115,11 @@ export class CdkMenuBar extends CdkMenuGroup implements Menu, AfterContentInit,
112115
this._keyManager.setLastItemActive();
113116
}
114117

118+
// In Ivy the `host` metadata will be merged, whereas in ViewEngine it is overridden. In order
119+
// to avoid double event listeners, we need to use `HostListener`. Once Ivy is the default, we
120+
// can move this back into `host`.
121+
// tslint:disable:no-host-decorator-in-concrete
122+
@HostListener('keydown', ['$event'])
115123
/**
116124
* Handle keyboard events, specifically changing the focused element and/or toggling the active
117125
* items menu.
@@ -248,6 +256,11 @@ export class CdkMenuBar extends CdkMenuGroup implements Menu, AfterContentInit,
248256
return this.orientation === 'horizontal';
249257
}
250258

259+
// In Ivy the `host` metadata will be merged, whereas in ViewEngine it is overridden. In order
260+
// to avoid double event listeners, we need to use `HostListener`. Once Ivy is the default, we
261+
// can move this back into `host`.
262+
// tslint:disable:no-host-decorator-in-concrete
263+
@HostListener('document:click', ['$event'])
251264
/** Close any open submenu if there was a click event which occurred outside the menu stack. */
252265
_closeOnBackgroundClick(event: MouseEvent) {
253266
if (this._hasOpenSubmenu()) {

src/cdk-experimental/menu/menu-item-checkbox.ts

Lines changed: 6 additions & 2 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 {Directive} from '@angular/core';
9+
import {Directive, HostListener} from '@angular/core';
1010
import {CdkMenuItemSelectable} from './menu-item-selectable';
1111
import {CdkMenuItem} from './menu-item';
1212

@@ -18,7 +18,6 @@ import {CdkMenuItem} from './menu-item';
1818
selector: '[cdkMenuItemCheckbox]',
1919
exportAs: 'cdkMenuItemCheckbox',
2020
host: {
21-
'(click)': 'trigger()',
2221
'type': 'button',
2322
'role': 'menuitemcheckbox',
2423
'[attr.aria-checked]': 'checked || null',
@@ -30,6 +29,11 @@ import {CdkMenuItem} from './menu-item';
3029
],
3130
})
3231
export class CdkMenuItemCheckbox extends CdkMenuItemSelectable {
32+
// In Ivy the `host` metadata will be merged, whereas in ViewEngine it is overridden. In order
33+
// to avoid double event listeners, we need to use `HostListener`. Once Ivy is the default, we
34+
// can move this back into `host`.
35+
// tslint:disable:no-host-decorator-in-concrete
36+
@HostListener('click')
3337
trigger() {
3438
super.trigger();
3539

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,16 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
import {UniqueSelectionDispatcher} from '@angular/cdk/collections';
9-
import {Directive, OnDestroy, ElementRef, Self, Optional, Inject, NgZone} from '@angular/core';
9+
import {
10+
Directive,
11+
OnDestroy,
12+
ElementRef,
13+
Self,
14+
Optional,
15+
Inject,
16+
NgZone,
17+
HostListener,
18+
} from '@angular/core';
1019
import {Directionality} from '@angular/cdk/bidi';
1120
import {CdkMenuItemSelectable} from './menu-item-selectable';
1221
import {CdkMenuItem} from './menu-item';
@@ -22,7 +31,6 @@ import {CDK_MENU, Menu} from './menu-interface';
2231
selector: '[cdkMenuItemRadio]',
2332
exportAs: 'cdkMenuItemRadio',
2433
host: {
25-
'(click)': 'trigger()',
2634
'type': 'button',
2735
'role': 'menuitemradio',
2836
'[attr.aria-checked]': 'checked || null',
@@ -60,6 +68,11 @@ export class CdkMenuItemRadio extends CdkMenuItemSelectable implements OnDestroy
6068
);
6169
}
6270

71+
// In Ivy the `host` metadata will be merged, whereas in ViewEngine it is overridden. In order
72+
// to avoid double event listeners, we need to use `HostListener`. Once Ivy is the default, we
73+
// can move this back into `host`.
74+
// tslint:disable:no-host-decorator-in-concrete
75+
@HostListener('click')
6376
/** Toggles the checked state of the radio-button. */
6477
trigger() {
6578
super.trigger();

src/cdk-experimental/menu/menu.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
Optional,
1919
OnInit,
2020
NgZone,
21+
HostListener,
2122
} from '@angular/core';
2223
import {FocusKeyManager, FocusOrigin} from '@angular/cdk/a11y';
2324
import {
@@ -51,7 +52,6 @@ import {getItemPointerEntries} from './item-pointer-entries';
5152
selector: '[cdkMenu]',
5253
exportAs: 'cdkMenu',
5354
host: {
54-
'(keydown)': '_handleKeyEvent($event)',
5555
'role': 'menu',
5656
'class': 'cdk-menu',
5757
'[attr.aria-orientation]': 'orientation',
@@ -136,6 +136,11 @@ export class CdkMenu extends CdkMenuGroup implements Menu, AfterContentInit, OnI
136136
this._keyManager.setLastItemActive();
137137
}
138138

139+
// In Ivy the `host` metadata will be merged, whereas in ViewEngine it is overridden. In order
140+
// to avoid double event listeners, we need to use `HostListener`. Once Ivy is the default, we
141+
// can move this back into `host`.
142+
// tslint:disable:no-host-decorator-in-concrete
143+
@HostListener('keydown', ['$event'])
139144
/** Handle keyboard events for the Menu. */
140145
_handleKeyEvent(event: KeyboardEvent) {
141146
const keyManager = this._keyManager;

0 commit comments

Comments
 (0)