-
Notifications
You must be signed in to change notification settings - Fork 6.8k
Description
Feature Description
When building a menu using aria/menu you have to have the following structure:
- an for cdkConnectedOverlay
- a container element with ngMenu
- another for ngMenuContent
- only then the actual ngMenuItem elements
This results in 4 structural levels that need to be repeated every time you need a new menu.
I attempted to create a shared menu component (similar to Angular Material’s MatMenu) in order to reduce this complexity to 2 levels and enable an API like:
<button appMenuTrigger #appMenuTrigger="appMenuTrigger" [appMenu]="appMenu.formatMenu()">
Open Menu
</button>
<app-menu #appMenu [appMenuTrigger]="appMenuTrigger">
<app-menu-item value="Mark as read">
<span class="icon material-symbols-outlined" aria-hidden="true">mark_email_read</span>
<span class="label">Mark as read</span>
</app-menu-item>
<app-menu-item value="Snooze">
<span class="icon material-symbols-outlined" aria-hidden="true">snooze</span>
<span class="label">Snooze</span>
</app-menu-item>
</app-menu>
Internally, app-menu wraps Menu and MenuContent:
@Component({
selector: 'app-menu',
imports: [OverlayModule, Menu, MenuContent],
template: `
<ng-template
[cdkConnectedOverlayOpen]="appMenuTrigger().menuTrigger.expanded()"
[cdkConnectedOverlay]="{ origin: appMenuTrigger().nativeElement, usePopover: 'inline' }"
[cdkConnectedOverlayPositions]="overlayPositions"
cdkAttachPopoverAsChild>
<div ngMenu class="menu" #formatMenu="ngMenu">
<ng-template ngMenuContent>
<ng-content />
</ng-template>
</div>
</ng-template>
`
})
export class AppMenu {
appMenuTrigger = input.required<AppMenuTrigger>();
formatMenu = viewChild<Menu<string>>('formatMenu');
overlayPositions: ConnectedPosition[] = [{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4}];
}
and menu items are wrapped like this:
@Component({
selector: 'app-menu-item',
template: '<ng-content />',
hostDirectives: [{ directive: MenuItem, inputs: ['value: value'] }]
})
export class AppMenuItem {}
Problem
With this approach, accessibility breaks (keyboard navigation no longer works correctly). This seems to happen because Menu cannot properly register MenuItem children when they are content projected.
Proposed enhancement
Expose a public API on Menu that allows the menu to "refresh" in order to register the items when they are content projected.
Use Case
No response