@@ -43,7 +43,7 @@ import {normalizePassiveListenerOptions} from '@angular/cdk/platform';
4343import { asapScheduler , merge , Observable , of as observableOf , Subscription } from 'rxjs' ;
4444import { delay , filter , take , takeUntil } from 'rxjs/operators' ;
4545import { _MatMenuBase , MenuCloseReason } from './menu' ;
46- import { throwMatMenuMissingError , throwMatMenuRecursiveError } from './menu-errors' ;
46+ import { throwMatMenuRecursiveError } from './menu-errors' ;
4747import { MatMenuItem } from './menu-item' ;
4848import { MAT_MENU_PANEL , MatMenuPanel } from './menu-panel' ;
4949import { MenuPositionX , MenuPositionY } from './menu-positions' ;
@@ -75,7 +75,7 @@ const passiveEventListenerOptions = normalizePassiveListenerOptions({passive: tr
7575
7676@Directive ( {
7777 host : {
78- 'aria-haspopup' : 'true' ,
78+ '[attr. aria-haspopup] ' : 'menu ? true : null ' ,
7979 '[attr.aria-expanded]' : 'menuOpen || null' ,
8080 '[attr.aria-controls]' : 'menuOpen ? menu.panelId : null' ,
8181 '(click)' : '_handleClick($event)' ,
@@ -117,19 +117,19 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
117117 * @breaking -change 8.0.0
118118 */
119119 @Input ( 'mat-menu-trigger-for' )
120- get _deprecatedMatMenuTriggerFor ( ) : MatMenuPanel {
120+ get _deprecatedMatMenuTriggerFor ( ) : MatMenuPanel | null {
121121 return this . menu ;
122122 }
123- set _deprecatedMatMenuTriggerFor ( v : MatMenuPanel ) {
123+ set _deprecatedMatMenuTriggerFor ( v : MatMenuPanel | null ) {
124124 this . menu = v ;
125125 }
126126
127127 /** References the menu instance that the trigger is associated with. */
128128 @Input ( 'matMenuTriggerFor' )
129- get menu ( ) {
129+ get menu ( ) : MatMenuPanel | null {
130130 return this . _menu ;
131131 }
132- set menu ( menu : MatMenuPanel ) {
132+ set menu ( menu : MatMenuPanel | null ) {
133133 if ( menu === this . _menu ) {
134134 return ;
135135 }
@@ -152,7 +152,7 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
152152 } ) ;
153153 }
154154 }
155- private _menu : MatMenuPanel ;
155+ private _menu : MatMenuPanel | null ;
156156
157157 /** Data to be passed along to any lazily-rendered content. */
158158 @Input ( 'matMenuTriggerData' ) menuData : any ;
@@ -244,7 +244,6 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
244244 }
245245
246246 ngAfterContentInit ( ) {
247- this . _checkMenu ( ) ;
248247 this . _handleHover ( ) ;
249248 }
250249
@@ -287,31 +286,31 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
287286
288287 /** Opens the menu. */
289288 openMenu ( ) : void {
290- if ( this . _menuOpen ) {
289+ const menu = this . menu ;
290+
291+ if ( this . _menuOpen || ! menu ) {
291292 return ;
292293 }
293294
294- this . _checkMenu ( ) ;
295-
296- const overlayRef = this . _createOverlay ( ) ;
295+ const overlayRef = this . _createOverlay ( menu ) ;
297296 const overlayConfig = overlayRef . getConfig ( ) ;
298297 const positionStrategy = overlayConfig . positionStrategy as FlexibleConnectedPositionStrategy ;
299298
300- this . _setPosition ( positionStrategy ) ;
299+ this . _setPosition ( menu , positionStrategy ) ;
301300 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 ) ) ;
304303
305- if ( this . menu . lazyContent ) {
306- this . menu . lazyContent . attach ( this . menuData ) ;
304+ if ( menu . lazyContent ) {
305+ menu . lazyContent . attach ( this . menuData ) ;
307306 }
308307
309308 this . _closingActionsSubscription = this . _menuClosingActions ( ) . subscribe ( ( ) => this . closeMenu ( ) ) ;
310- this . _initMenu ( ) ;
309+ this . _initMenu ( menu ) ;
311310
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 ( ( ) => {
315314 // Re-adjust the position without locking when the amount of items
316315 // changes so that the overlay is allowed to pick a new optimal position.
317316 positionStrategy . withLockedPosition ( false ) . reapplyLastPosition ( ) ;
@@ -322,7 +321,7 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
322321
323322 /** Closes the menu. */
324323 closeMenu ( ) : void {
325- this . menu . close . emit ( ) ;
324+ this . menu ? .close . emit ( ) ;
326325 }
327326
328327 /**
@@ -386,37 +385,34 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
386385 }
387386 } else {
388387 this . _setIsMenuOpen ( false ) ;
389-
390- if ( menu . lazyContent ) {
391- menu . lazyContent . detach ( ) ;
392- }
388+ menu ?. lazyContent ?. detach ( ) ;
393389 }
394390 }
395391
396392 /**
397393 * This method sets the menu state to open and focuses the first item if
398394 * the menu was opened via the keyboard.
399395 */
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' ) ;
405401 this . _setIsMenuOpen ( true ) ;
406402 }
407403
408404 /** 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 ) {
411407 let depth = 0 ;
412- let parentMenu = this . menu . parentMenu ;
408+ let parentMenu = menu . parentMenu ;
413409
414410 while ( parentMenu ) {
415411 depth ++ ;
416412 parentMenu = parentMenu . parentMenu ;
417413 }
418414
419- this . menu . setElevation ( depth ) ;
415+ menu . setElevation ( depth ) ;
420416 }
421417 }
422418
@@ -430,24 +426,17 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
430426 }
431427 }
432428
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-
443429 /**
444430 * This method creates the overlay from the provided menu's template and saves its
445431 * OverlayRef so that it can be attached to the DOM when openMenu is called.
446432 */
447- private _createOverlay ( ) : OverlayRef {
433+ private _createOverlay ( menu : MatMenuPanel ) : OverlayRef {
448434 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+ ) ;
451440 this . _overlayRef = this . _overlay . create ( config ) ;
452441
453442 // Consume the `keydownEvents` in order to prevent them from going to another overlay.
@@ -463,16 +452,16 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
463452 * This method builds the configuration object needed to create the overlay, the OverlayState.
464453 * @returns OverlayConfig
465454 */
466- private _getOverlayConfig ( ) : OverlayConfig {
455+ private _getOverlayConfig ( menu : MatMenuPanel ) : OverlayConfig {
467456 return new OverlayConfig ( {
468457 positionStrategy : this . _overlay
469458 . position ( )
470459 . flexibleConnectedTo ( this . _element )
471460 . withLockedPosition ( )
472461 . withGrowAfterOpen ( )
473462 . 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 ,
476465 scrollStrategy : this . _scrollStrategy ( ) ,
477466 direction : this . _dir ,
478467 } ) ;
@@ -483,8 +472,8 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
483472 * on the menu based on the new position. This ensures the animation origin is always
484473 * correct, even if a fallback position is used for the overlay.
485474 */
486- private _subscribeToPositions ( position : FlexibleConnectedPositionStrategy ) : void {
487- if ( this . menu . setPositionClasses ) {
475+ private _subscribeToPositions ( menu : MatMenuPanel , position : FlexibleConnectedPositionStrategy ) {
476+ if ( menu . setPositionClasses ) {
488477 position . positionChanges . subscribe ( change => {
489478 const posX : MenuPositionX = change . connectionPair . overlayX === 'start' ? 'after' : 'before' ;
490479 const posY : MenuPositionY = change . connectionPair . overlayY === 'top' ? 'below' : 'above' ;
@@ -493,9 +482,9 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
493482 // `positionChanges` fires outside of the `ngZone` and `setPositionClasses` might be
494483 // updating something in the view so we need to bring it back in.
495484 if ( this . _ngZone ) {
496- this . _ngZone . run ( ( ) => this . menu . setPositionClasses ! ( posX , posY ) ) ;
485+ this . _ngZone . run ( ( ) => menu . setPositionClasses ! ( posX , posY ) ) ;
497486 } else {
498- this . menu . setPositionClasses ! ( posX , posY ) ;
487+ menu . setPositionClasses ! ( posX , posY ) ;
499488 }
500489 } ) ;
501490 }
@@ -506,12 +495,12 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
506495 * so the overlay connects with the trigger correctly.
507496 * @param positionStrategy Strategy whose position to update.
508497 */
509- private _setPosition ( positionStrategy : FlexibleConnectedPositionStrategy ) {
498+ private _setPosition ( menu : MatMenuPanel , positionStrategy : FlexibleConnectedPositionStrategy ) {
510499 let [ originX , originFallbackX ] : HorizontalConnectionPos [ ] =
511- this . menu . xPosition === 'before' ? [ 'end' , 'start' ] : [ 'start' , 'end' ] ;
500+ menu . xPosition === 'before' ? [ 'end' , 'start' ] : [ 'start' , 'end' ] ;
512501
513502 let [ overlayY , overlayFallbackY ] : VerticalConnectionPos [ ] =
514- this . menu . yPosition === 'above' ? [ 'bottom' , 'top' ] : [ 'top' , 'bottom' ] ;
503+ menu . yPosition === 'above' ? [ 'bottom' , 'top' ] : [ 'top' , 'bottom' ] ;
515504
516505 let [ originY , originFallbackY ] = [ overlayY , overlayFallbackY ] ;
517506 let [ overlayX , overlayFallbackX ] = [ originX , originFallbackX ] ;
@@ -520,10 +509,10 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
520509 if ( this . triggersSubmenu ( ) ) {
521510 // When the menu is a sub-menu, it should always align itself
522511 // 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' ;
524513 originFallbackX = overlayX = originX === 'end' ? 'start' : 'end' ;
525514 offsetY = overlayY === 'bottom' ? MENU_PANEL_TOP_PADDING : - MENU_PANEL_TOP_PADDING ;
526- } else if ( ! this . menu . overlapTrigger ) {
515+ } else if ( ! menu . overlapTrigger ) {
527516 originY = overlayY === 'top' ? 'bottom' : 'top' ;
528517 originFallbackY = overlayFallbackY === 'top' ? 'bottom' : 'top' ;
529518 }
@@ -644,12 +633,12 @@ export abstract class _MatMenuTriggerBase implements AfterContentInit, OnDestroy
644633 }
645634
646635 /** Gets the portal that should be attached to the overlay. */
647- private _getPortal ( ) : TemplatePortal {
636+ private _getPortal ( menu : MatMenuPanel ) : TemplatePortal {
648637 // Note that we can avoid this check by keeping the portal on the menu panel.
649638 // While it would be cleaner, we'd have to introduce another required method on
650639 // `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 ) ;
653642 }
654643
655644 return this . _portal ;
0 commit comments