I noticed this behavior in Angular Material that if I move a submenu in a separate component, it doesn't open on mouseover as it would if it were defined in the same component as its parent menu.
For example, this menu opens on mouseover:
<button mat-button [matMenuTriggerFor]="menu">
Menu
</button>
<mat-menu #menu="matMenu">
<button mat-menu-item [matMenuTriggerFor]="submenu">
Submenu
</button>
<mat-menu #submenu>
<button mat-menu-item>Submenu Item</button>
</mat-menu>
</mat-menu>
This menu doesn't open on mouseover:
// menu.component.html
<button mat-button [matMenuTriggerFor]="menu">
Menu
</button>
<mat-menu #menu="matMenu">
<app-submenu></app-submenu>
</mat-menu>
// submenu.component.html
<button mat-menu-item [matMenuTriggerFor]="submenu">
Submenu
</button>
<mat-menu #submenu>
<button mat-menu-item>Submenu Item</button>
</mat-menu>
StackBlitz example
Also the submenu alignment seems to be a bit different. Should nested menus be always in the same component?
Angular Material uses @ContentChildren to enumerate all of its child menu items:
@ContentChildren(MatMenuItem, {descendants: true}) _allItems: QueryList<MatMenuItem>;
(See https://github.com/angular/components/blob/14.2.x/src/material/menu/menu.ts#L110 for the v14 implementation)
The issue here involves how @ContentChildren works. If you look at the Angular documentation (https://angular.io/api/core/ContentChildren#description) you'll see this caveat:
Does not retrieve elements or directives that are in other components' templates, since a component's template is always a black box to its ancestors.
What this means is that the Menu component doesn't know about any menu items that are in a child component (<app-submenu></app-submenu> in this case). Those items don't get returned in the @ContentChildren query and don't get the mouse listeners/etc. attached for them to work like "proper" menu items.
Sadly, the only solution I've found so far is to avoid custom components that provide menu items. All of the menu items have to be defined in the same HTML template, although you may be able to use ng-template to define reusable pieces as needed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With