@@ -43,7 +43,7 @@ import {normalizePassiveListenerOptions} from '@angular/cdk/platform';
43
43
import { asapScheduler , merge , Observable , of as observableOf , Subscription } from 'rxjs' ;
44
44
import { delay , filter , take , takeUntil } from 'rxjs/operators' ;
45
45
import { _MatMenuBase , MenuCloseReason } from './menu' ;
46
- import { throwMatMenuMissingError , throwMatMenuRecursiveError } from './menu-errors' ;
46
+ import { throwMatMenuRecursiveError } from './menu-errors' ;
47
47
import { MatMenuItem } from './menu-item' ;
48
48
import { MAT_MENU_PANEL , MatMenuPanel } from './menu-panel' ;
49
49
import { MenuPositionX , MenuPositionY } from './menu-positions' ;
@@ -75,7 +75,7 @@ const passiveEventListenerOptions = normalizePassiveListenerOptions({passive: tr
75
75
76
76
@Directive ( {
77
77
host : {
78
- 'aria-haspopup' : 'true' ,
78
+ '[attr. aria-haspopup] ' : 'menu ? true : null ' ,
79
79
'[attr.aria-expanded]' : 'menuOpen || null' ,
80
80
'[attr.aria-controls]' : 'menuOpen ? menu.panelId : null' ,
81
81
'(click)' : '_handleClick($event)' ,
@@ -117,19 +117,19 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
117
117
* @breaking -change 8.0.0
118
118
*/
119
119
@Input ( 'mat-menu-trigger-for' )
120
- get _deprecatedMatMenuTriggerFor ( ) : MatMenuPanel {
120
+ get _deprecatedMatMenuTriggerFor ( ) : MatMenuPanel | null {
121
121
return this . menu ;
122
122
}
123
- set _deprecatedMatMenuTriggerFor ( v : MatMenuPanel ) {
123
+ set _deprecatedMatMenuTriggerFor ( v : MatMenuPanel | null ) {
124
124
this . menu = v ;
125
125
}
126
126
127
127
/** References the menu instance that the trigger is associated with. */
128
128
@Input ( 'matMenuTriggerFor' )
129
- get menu ( ) {
129
+ get menu ( ) : MatMenuPanel | null {
130
130
return this . _menu ;
131
131
}
132
- set menu ( menu : MatMenuPanel ) {
132
+ set menu ( menu : MatMenuPanel | null ) {
133
133
if ( menu === this . _menu ) {
134
134
return ;
135
135
}
@@ -152,7 +152,7 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
152
152
} ) ;
153
153
}
154
154
}
155
- private _menu : MatMenuPanel ;
155
+ private _menu : MatMenuPanel | null ;
156
156
157
157
/** Data to be passed along to any lazily-rendered content. */
158
158
@Input ( 'matMenuTriggerData' ) menuData : any ;
@@ -244,7 +244,6 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
244
244
}
245
245
246
246
ngAfterContentInit ( ) {
247
- this . _checkMenu ( ) ;
248
247
this . _handleHover ( ) ;
249
248
}
250
249
@@ -287,31 +286,31 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
287
286
288
287
/** Opens the menu. */
289
288
openMenu ( ) : void {
290
- if ( this . _menuOpen ) {
289
+ const menu = this . menu ;
290
+
291
+ if ( this . _menuOpen || ! menu ) {
291
292
return ;
292
293
}
293
294
294
- this . _checkMenu ( ) ;
295
-
296
- const overlayRef = this . _createOverlay ( ) ;
295
+ const overlayRef = this . _createOverlay ( menu ) ;
297
296
const overlayConfig = overlayRef . getConfig ( ) ;
298
297
const positionStrategy = overlayConfig . positionStrategy as FlexibleConnectedPositionStrategy ;
299
298
300
- this . _setPosition ( positionStrategy ) ;
299
+ this . _setPosition ( menu , positionStrategy ) ;
301
300
overlayConfig . hasBackdrop =
302
- this . menu . hasBackdrop == null ? ! this . triggersSubmenu ( ) : this . menu . hasBackdrop ;
303
- overlayRef . attach ( this . _getPortal ( ) ) ;
301
+ menu . hasBackdrop == null ? ! this . triggersSubmenu ( ) : menu . hasBackdrop ;
302
+ overlayRef . attach ( this . _getPortal ( menu ) ) ;
304
303
305
- if ( this . menu . lazyContent ) {
306
- this . menu . lazyContent . attach ( this . menuData ) ;
304
+ if ( menu . lazyContent ) {
305
+ menu . lazyContent . attach ( this . menuData ) ;
307
306
}
308
307
309
308
this . _closingActionsSubscription = this . _menuClosingActions ( ) . subscribe ( ( ) => this . closeMenu ( ) ) ;
310
- this . _initMenu ( ) ;
309
+ this . _initMenu ( menu ) ;
311
310
312
- if ( this . menu instanceof _MatMenuBase ) {
313
- this . menu . _startAnimation ( ) ;
314
- this . menu . _directDescendantItems . changes . pipe ( takeUntil ( this . menu . close ) ) . subscribe ( ( ) => {
311
+ if ( menu instanceof _MatMenuBase ) {
312
+ menu . _startAnimation ( ) ;
313
+ menu . _directDescendantItems . changes . pipe ( takeUntil ( menu . close ) ) . subscribe ( ( ) => {
315
314
// Re-adjust the position without locking when the amount of items
316
315
// changes so that the overlay is allowed to pick a new optimal position.
317
316
positionStrategy . withLockedPosition ( false ) . reapplyLastPosition ( ) ;
@@ -322,7 +321,7 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
322
321
323
322
/** Closes the menu. */
324
323
closeMenu ( ) : void {
325
- this . menu . close . emit ( ) ;
324
+ this . menu ? .close . emit ( ) ;
326
325
}
327
326
328
327
/**
@@ -386,37 +385,34 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
386
385
}
387
386
} else {
388
387
this . _setIsMenuOpen ( false ) ;
389
-
390
- if ( menu . lazyContent ) {
391
- menu . lazyContent . detach ( ) ;
392
- }
388
+ menu ?. lazyContent ?. detach ( ) ;
393
389
}
394
390
}
395
391
396
392
/**
397
393
* This method sets the menu state to open and focuses the first item if
398
394
* the menu was opened via the keyboard.
399
395
*/
400
- private _initMenu ( ) : void {
401
- this . menu . parentMenu = this . triggersSubmenu ( ) ? this . _parentMaterialMenu : undefined ;
402
- this . menu . direction = this . dir ;
403
- this . _setMenuElevation ( ) ;
404
- this . menu . focusFirstItem ( this . _openedBy || 'program' ) ;
396
+ private _initMenu ( menu : MatMenuPanel ) : void {
397
+ menu . parentMenu = this . triggersSubmenu ( ) ? this . _parentMaterialMenu : undefined ;
398
+ menu . direction = this . dir ;
399
+ this . _setMenuElevation ( menu ) ;
400
+ menu . focusFirstItem ( this . _openedBy || 'program' ) ;
405
401
this . _setIsMenuOpen ( true ) ;
406
402
}
407
403
408
404
/** Updates the menu elevation based on the amount of parent menus that it has. */
409
- private _setMenuElevation ( ) : void {
410
- if ( this . menu . setElevation ) {
405
+ private _setMenuElevation ( menu : MatMenuPanel ) : void {
406
+ if ( menu . setElevation ) {
411
407
let depth = 0 ;
412
- let parentMenu = this . menu . parentMenu ;
408
+ let parentMenu = menu . parentMenu ;
413
409
414
410
while ( parentMenu ) {
415
411
depth ++ ;
416
412
parentMenu = parentMenu . parentMenu ;
417
413
}
418
414
419
- this . menu . setElevation ( depth ) ;
415
+ menu . setElevation ( depth ) ;
420
416
}
421
417
}
422
418
@@ -430,24 +426,17 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
430
426
}
431
427
}
432
428
433
- /**
434
- * This method checks that a valid instance of MatMenu has been passed into
435
- * matMenuTriggerFor. If not, an exception is thrown.
436
- */
437
- private _checkMenu ( ) {
438
- if ( ! this . menu && ( typeof ngDevMode === 'undefined' || ngDevMode ) ) {
439
- throwMatMenuMissingError ( ) ;
440
- }
441
- }
442
-
443
429
/**
444
430
* This method creates the overlay from the provided menu's template and saves its
445
431
* OverlayRef so that it can be attached to the DOM when openMenu is called.
446
432
*/
447
- private _createOverlay ( ) : OverlayRef {
433
+ private _createOverlay ( menu : MatMenuPanel ) : OverlayRef {
448
434
if ( ! this . _overlayRef ) {
449
- const config = this . _getOverlayConfig ( ) ;
450
- this . _subscribeToPositions ( config . positionStrategy as FlexibleConnectedPositionStrategy ) ;
435
+ const config = this . _getOverlayConfig ( menu ) ;
436
+ this . _subscribeToPositions (
437
+ menu ,
438
+ config . positionStrategy as FlexibleConnectedPositionStrategy ,
439
+ ) ;
451
440
this . _overlayRef = this . _overlay . create ( config ) ;
452
441
453
442
// Consume the `keydownEvents` in order to prevent them from going to another overlay.
@@ -463,16 +452,16 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
463
452
* This method builds the configuration object needed to create the overlay, the OverlayState.
464
453
* @returns OverlayConfig
465
454
*/
466
- private _getOverlayConfig ( ) : OverlayConfig {
455
+ private _getOverlayConfig ( menu : MatMenuPanel ) : OverlayConfig {
467
456
return new OverlayConfig ( {
468
457
positionStrategy : this . _overlay
469
458
. position ( )
470
459
. flexibleConnectedTo ( this . _element )
471
460
. withLockedPosition ( )
472
461
. withGrowAfterOpen ( )
473
462
. withTransformOriginOn ( '.mat-menu-panel, .mat-mdc-menu-panel' ) ,
474
- backdropClass : this . menu . backdropClass || 'cdk-overlay-transparent-backdrop' ,
475
- panelClass : this . menu . overlayPanelClass ,
463
+ backdropClass : menu . backdropClass || 'cdk-overlay-transparent-backdrop' ,
464
+ panelClass : menu . overlayPanelClass ,
476
465
scrollStrategy : this . _scrollStrategy ( ) ,
477
466
direction : this . _dir ,
478
467
} ) ;
@@ -483,8 +472,8 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
483
472
* on the menu based on the new position. This ensures the animation origin is always
484
473
* correct, even if a fallback position is used for the overlay.
485
474
*/
486
- private _subscribeToPositions ( position : FlexibleConnectedPositionStrategy ) : void {
487
- if ( this . menu . setPositionClasses ) {
475
+ private _subscribeToPositions ( menu : MatMenuPanel , position : FlexibleConnectedPositionStrategy ) {
476
+ if ( menu . setPositionClasses ) {
488
477
position . positionChanges . subscribe ( change => {
489
478
const posX : MenuPositionX = change . connectionPair . overlayX === 'start' ? 'after' : 'before' ;
490
479
const posY : MenuPositionY = change . connectionPair . overlayY === 'top' ? 'below' : 'above' ;
@@ -493,9 +482,9 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
493
482
// `positionChanges` fires outside of the `ngZone` and `setPositionClasses` might be
494
483
// updating something in the view so we need to bring it back in.
495
484
if ( this . _ngZone ) {
496
- this . _ngZone . run ( ( ) => this . menu . setPositionClasses ! ( posX , posY ) ) ;
485
+ this . _ngZone . run ( ( ) => menu . setPositionClasses ! ( posX , posY ) ) ;
497
486
} else {
498
- this . menu . setPositionClasses ! ( posX , posY ) ;
487
+ menu . setPositionClasses ! ( posX , posY ) ;
499
488
}
500
489
} ) ;
501
490
}
@@ -506,12 +495,12 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
506
495
* so the overlay connects with the trigger correctly.
507
496
* @param positionStrategy Strategy whose position to update.
508
497
*/
509
- private _setPosition ( positionStrategy : FlexibleConnectedPositionStrategy ) {
498
+ private _setPosition ( menu : MatMenuPanel , positionStrategy : FlexibleConnectedPositionStrategy ) {
510
499
let [ originX , originFallbackX ] : HorizontalConnectionPos [ ] =
511
- this . menu . xPosition === 'before' ? [ 'end' , 'start' ] : [ 'start' , 'end' ] ;
500
+ menu . xPosition === 'before' ? [ 'end' , 'start' ] : [ 'start' , 'end' ] ;
512
501
513
502
let [ overlayY , overlayFallbackY ] : VerticalConnectionPos [ ] =
514
- this . menu . yPosition === 'above' ? [ 'bottom' , 'top' ] : [ 'top' , 'bottom' ] ;
503
+ menu . yPosition === 'above' ? [ 'bottom' , 'top' ] : [ 'top' , 'bottom' ] ;
515
504
516
505
let [ originY , originFallbackY ] = [ overlayY , overlayFallbackY ] ;
517
506
let [ overlayX , overlayFallbackX ] = [ originX , originFallbackX ] ;
@@ -520,10 +509,10 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
520
509
if ( this . triggersSubmenu ( ) ) {
521
510
// When the menu is a sub-menu, it should always align itself
522
511
// to the edges of the trigger, instead of overlapping it.
523
- overlayFallbackX = originX = this . menu . xPosition === 'before' ? 'start' : 'end' ;
512
+ overlayFallbackX = originX = menu . xPosition === 'before' ? 'start' : 'end' ;
524
513
originFallbackX = overlayX = originX === 'end' ? 'start' : 'end' ;
525
514
offsetY = overlayY === 'bottom' ? MENU_PANEL_TOP_PADDING : - MENU_PANEL_TOP_PADDING ;
526
- } else if ( ! this . menu . overlapTrigger ) {
515
+ } else if ( ! menu . overlapTrigger ) {
527
516
originY = overlayY === 'top' ? 'bottom' : 'top' ;
528
517
originFallbackY = overlayFallbackY === 'top' ? 'bottom' : 'top' ;
529
518
}
@@ -644,12 +633,12 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
644
633
}
645
634
646
635
/** Gets the portal that should be attached to the overlay. */
647
- private _getPortal ( ) : TemplatePortal {
636
+ private _getPortal ( menu : MatMenuPanel ) : TemplatePortal {
648
637
// Note that we can avoid this check by keeping the portal on the menu panel.
649
638
// While it would be cleaner, we'd have to introduce another required method on
650
639
// `MatMenuPanel`, making it harder to consume.
651
- if ( ! this . _portal || this . _portal . templateRef !== this . menu . templateRef ) {
652
- this . _portal = new TemplatePortal ( this . menu . templateRef , this . _viewContainerRef ) ;
640
+ if ( ! this . _portal || this . _portal . templateRef !== menu . templateRef ) {
641
+ this . _portal = new TemplatePortal ( menu . templateRef , this . _viewContainerRef ) ;
653
642
}
654
643
655
644
return this . _portal ;
0 commit comments