From a2cf3f6cc88ec3c4ad5596090a3b4c8de8b8f5d4 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Wed, 12 Oct 2022 09:07:36 +0200 Subject: [PATCH] feat(cdk/menu): add support for passing in data to the menu template (#25778) Adds the `cdkMenuTriggerData` and `cdkContextMenuTriggerData` inputs that allow the consumer of a CDK menu to pass context data to the menu template. Fixes #25708. --- src/cdk/menu/context-menu-trigger.spec.ts | 29 ++++++++++++++++++++++ src/cdk/menu/context-menu-trigger.ts | 6 ++++- src/cdk/menu/menu-trigger-base.ts | 5 +++- src/cdk/menu/menu-trigger.spec.ts | 30 +++++++++++++++++++++++ src/cdk/menu/menu-trigger.ts | 6 ++++- tools/public_api_guard/cdk/menu.md | 5 ++-- 6 files changed, 76 insertions(+), 5 deletions(-) diff --git a/src/cdk/menu/context-menu-trigger.spec.ts b/src/cdk/menu/context-menu-trigger.spec.ts index 387236041ced..442568efed54 100644 --- a/src/cdk/menu/context-menu-trigger.spec.ts +++ b/src/cdk/menu/context-menu-trigger.spec.ts @@ -422,6 +422,21 @@ describe('CdkContextMenuTrigger', () => { expect(fixture.componentInstance.menus.length).toBe(1); }); }); + + it('should be able to pass data to the menu via the template context', () => { + TestBed.configureTestingModule({ + imports: [CdkMenuModule], + declarations: [ContextTriggerWithData], + }).compileComponents(); + + const fixture = TestBed.createComponent(ContextTriggerWithData); + fixture.componentInstance.menuData = {message: 'Hello!'}; + fixture.detectChanges(); + dispatchMouseEvent(fixture.componentInstance.triggerElement.nativeElement, 'contextmenu'); + fixture.detectChanges(); + + expect(document.querySelector('.test-menu')?.textContent).toBe('Hello!'); + }); }); @Component({ @@ -552,3 +567,17 @@ class MenuBarAndContextTriggerShareMenu { @ViewChild(CdkContextMenuTrigger) contextTrigger: CdkContextMenuTrigger; @ViewChildren(CdkMenu) menus: QueryList; } + +@Component({ + template: ` +
+ + +
{{message}}
+
+ `, +}) +class ContextTriggerWithData { + @ViewChild(CdkContextMenuTrigger, {read: ElementRef}) triggerElement: ElementRef; + menuData: unknown; +} diff --git a/src/cdk/menu/context-menu-trigger.ts b/src/cdk/menu/context-menu-trigger.ts index 6f2b176670bc..593d97c117c3 100644 --- a/src/cdk/menu/context-menu-trigger.ts +++ b/src/cdk/menu/context-menu-trigger.ts @@ -61,7 +61,11 @@ export type ContextMenuCoordinates = {x: number; y: number}; '[attr.data-cdk-menu-stack-id]': 'null', '(contextmenu)': '_openOnContextMenu($event)', }, - inputs: ['menuTemplateRef: cdkContextMenuTriggerFor', 'menuPosition: cdkContextMenuPosition'], + inputs: [ + 'menuTemplateRef: cdkContextMenuTriggerFor', + 'menuPosition: cdkContextMenuPosition', + 'menuData: cdkContextMenuTriggerData', + ], outputs: ['opened: cdkContextMenuOpened', 'closed: cdkContextMenuClosed'], providers: [ {provide: MENU_TRIGGER, useExisting: CdkContextMenuTrigger}, diff --git a/src/cdk/menu/menu-trigger-base.ts b/src/cdk/menu/menu-trigger-base.ts index f6714a3973f7..2f4f3180acea 100644 --- a/src/cdk/menu/menu-trigger-base.ts +++ b/src/cdk/menu/menu-trigger-base.ts @@ -60,6 +60,9 @@ export abstract class CdkMenuTriggerBase implements OnDestroy { /** Template reference variable to the menu this trigger opens */ menuTemplateRef: TemplateRef; + /** Context data to be passed along to the menu template */ + menuData: unknown; + /** A reference to the overlay which manages the triggered menu */ protected overlayRef: OverlayRef | null = null; @@ -105,7 +108,7 @@ export abstract class CdkMenuTriggerBase implements OnDestroy { this._menuPortal = new TemplatePortal( this.menuTemplateRef, this.viewContainerRef, - undefined, + this.menuData, this._getChildMenuInjector(), ); } diff --git a/src/cdk/menu/menu-trigger.spec.ts b/src/cdk/menu/menu-trigger.spec.ts index 11a03dcee273..67bfa66f3793 100644 --- a/src/cdk/menu/menu-trigger.spec.ts +++ b/src/cdk/menu/menu-trigger.spec.ts @@ -454,6 +454,21 @@ describe('MenuTrigger', () => { expect(nativeMenus.length).toBe(1); }); }); + + it('should be able to pass data to the menu via the template context', () => { + TestBed.configureTestingModule({ + imports: [CdkMenuModule], + declarations: [TriggerWithData], + }).compileComponents(); + + const fixture = TestBed.createComponent(TriggerWithData); + fixture.componentInstance.menuData = {message: 'Hello!'}; + fixture.detectChanges(); + fixture.nativeElement.querySelector('button').click(); + fixture.detectChanges(); + + expect(document.querySelector('.test-menu')?.textContent).toBe('Hello!'); + }); }); @Component({ @@ -572,3 +587,18 @@ class StandaloneTriggerWithInlineMenu { @ViewChild('inline_item', {read: ElementRef}) nativeInlineItem: ElementRef; @ViewChildren(CdkMenu, {read: ElementRef}) nativeMenus: QueryList; } + +@Component({ + template: ` + + + +
{{message}}
+
+ `, +}) +class TriggerWithData { + menuData: unknown; +} diff --git a/src/cdk/menu/menu-trigger.ts b/src/cdk/menu/menu-trigger.ts index ed32279d88b5..9ae33f9004a0 100644 --- a/src/cdk/menu/menu-trigger.ts +++ b/src/cdk/menu/menu-trigger.ts @@ -51,7 +51,11 @@ import {CdkMenuTriggerBase, MENU_TRIGGER} from './menu-trigger-base'; '(keydown)': '_toggleOnKeydown($event)', '(click)': 'toggle()', }, - inputs: ['menuTemplateRef: cdkMenuTriggerFor', 'menuPosition: cdkMenuPosition'], + inputs: [ + 'menuTemplateRef: cdkMenuTriggerFor', + 'menuPosition: cdkMenuPosition', + 'menuData: cdkMenuTriggerData', + ], outputs: ['opened: cdkMenuOpened', 'closed: cdkMenuClosed'], providers: [ {provide: MENU_TRIGGER, useExisting: CdkMenuTrigger}, diff --git a/tools/public_api_guard/cdk/menu.md b/tools/public_api_guard/cdk/menu.md index a457cc2d5ab5..97ce8ac99569 100644 --- a/tools/public_api_guard/cdk/menu.md +++ b/tools/public_api_guard/cdk/menu.md @@ -40,7 +40,7 @@ export class CdkContextMenuTrigger extends CdkMenuTriggerBase implements OnDestr open(coordinates: ContextMenuCoordinates): void; _openOnContextMenu(event: MouseEvent): void; // (undocumented) - static ɵdir: i0.ɵɵDirectiveDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration; } @@ -203,7 +203,7 @@ export class CdkMenuTrigger extends CdkMenuTriggerBase implements OnDestroy { toggle(): void; _toggleOnKeydown(event: KeyboardEvent): void; // (undocumented) - static ɵdir: i0.ɵɵDirectiveDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration; } @@ -217,6 +217,7 @@ export abstract class CdkMenuTriggerBase implements OnDestroy { readonly injector: Injector; protected isElementInsideMenuStack(element: Element): boolean; isOpen(): boolean; + menuData: unknown; menuPosition: ConnectedPosition[]; protected readonly menuStack: MenuStack; menuTemplateRef: TemplateRef;