Open AlexBokhankovich opened 5 years ago
That's an interesting problem. I've never had to do that before.
You could try doing this:
<ng-template contextMenuItem></ng-template>
<!-- as many of these as you need, or use an *ngFor -->
@ViewChild(ContextMenuItemDirective) contextMenuItemDirectives: QueryList<ContextMenuItemDirective>;
createContextMenu(): any {
if (!this.contextMenuItems || this.contextMenuItems.length === 0) {
return;
}
const contextMenuComponentFactory = this.componentFactoryResolver.resolveComponentFactory(ContextMenuComponent);
const contextMenuCompRef = this.viewContainerRef.createComponent(contextMenuComponentFactory);
const contextMenuComp = contextMenuCompRef.instance;
contextMenuComp.menuItems = this.contextMenuItemDirectives;
}
I'm not sure if that will work.
I also found a more generic way to add ng-content
to a dynamically created component:
https://blog.ng-book.com/dynamic-components-with-content-projection-in-angular/
I'd use the Handling Templates
strategy in that article. And have the template look like this:
<ng-template #contextMenuItems>
<ng-template *ngFor="let action of contextMenuItems" contextMenuItem let-item
[visible]="action.visible" [enabled]="action.enabled" [divider]="action.divider"
(execute)="action.click($event.item)">
{{ action.html($event.item) }}
</ng-template>
</ng-template>
Good luck! Let me know if you get it working. I'm curious.
Thanks a lot for your assistance but this would not work in my case since I have a complex components that extends each other and I need to add this logic at the most low level component, that is extended by about 20 components. As angular components doesn't not support template inheritance, I can not use any template code. That is why I'm trying to do this programmatically. So, now I see two possible solutions: add template markup to each of my components or somehow inject menus dynamically. I've already use dynamically created components widely in my app. In every component I expose ViewContainerRef so other components can use it to dynamically inject needed components. In your component I see 2 things that could porevent from injecting it dynamically I a way I did before: -it doesn't expose ViewContainerRef -it uses ng-template and directive for menu items.
I think addind public ViewContainerRef to ContextMenuComponent and adding support for menu items components (not a directives) would let use dynamic injection. So, in this case we can dynamically create ContextMenuComponent, and using its ViewContainerRef inject as many MenuItemsComponents as we need.
You could try passing the TemplateRef down as an Input to whatever component needs it.
I’m open to making a public ViewContainerRef, but I don’t want to change ContextMenuItems from directives to components. It’s not worth making a breaking change for a minority use case. On Dec 7, 2018, 1:01 AM -0500, Alex Bokhankovich notifications@github.com, wrote:
Thanks a lot for your assistance but this would not work in my case since I have a complex components that extends each other and I need to add this logic at the most low level component, that is extended by about 20 components. As angular components doesn't not support template inheritance, I can not use any template code. That is why I'm trying to do this programmatically. So, now I see two possible solutions: add template markup to each of my components or somehow inject menus dynamically. I've already use dynamically created components widely in my app. In every component I expose ViewContainerRef so other components can use it to dynamically inject needed components. In your component I see 2 things that could porevent from injecting it dynamically I a way I did before: -it doesn't expose ViewContainerRef -it uses ng-template and directive for menu items. I think addind public ViewContainerRef to ContextMenuComponent and adding support for menu items components (not a directives) would let use dynamic injection. So, in this case we can dynamically create ContextMenuComponent, and using its ViewContainerRef inject as many MenuItemsComponents as we need. — You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.
I've solved this by creating component, that I inject in every place where I need custom context menu
import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core';
import { ContextMenuComponent } from 'ngx-contextmenu';
import { TypeChecker } from '../../animation/type-checker';
import { Point } from '../../models/point';
import { ReceptorAction } from '../actions/receptor-action';
import { ContextMenuItem } from './context-menu-item';
@Component({
selector: 'app-context-menu-receptor',
template: '<context-menu>
<ng-template contextMenuItem
*ngFor="let menuItem of menuItems"
(execute)="execReceptorAction($event.item, menuItem.action)">
{{menuItem.name}}
</ng-template>
</context-menu>',
styleUrls: ['./context-menu-receptor.component.scss']
})
export class ContextMenuReceptorComponent implements OnInit {
@ViewChild(ContextMenuComponent) public contextMenu: ContextMenuComponent;
@Output() receptorTriggered: EventEmitter < ReceptorAction > = new EventEmitter();
menuItems: ContextMenuItem[];
constructor() {}
ngOnInit() {}
execReceptorAction(position: Point, receptorAction: ReceptorAction) {
if (TypeChecker.instanceOfWindowOpenAction(receptorAction)) {
receptorAction.position = position;
}
this.receptorTriggered.emit(receptorAction)
}
}
and in my component I inject this and set it's menu items
createContextMenu(): any {
if (!this.contextMenuItems || this.contextMenuItems.length === 0) {
return;
}
const contextMenuComponentFactory = this.componentFactoryResolver.resolveComponentFactory(ContextMenuReceptorComponent);
const contextMenuCompRef = this.viewContainerRef.createComponent(contextMenuComponentFactory);
const contextMenuComp = contextMenuCompRef.instance;
contextMenuComp.menuItems = this.contextMenuItems;
this.contextMenu = contextMenuComp.contextMenu;
contextMenuComp.receptorTriggered.subscribe((action) => {
this.receptorTriggered.emit(action);
})
contextMenuComp.ngOnInit();
this.viewContainerRef.element.nativeElement.addEventListener('contextmenu', ($event, item) => {
this.onContextMenu($event, this.contextMenuItems);
});
}
private onContextMenu($event: MouseEvent, item: any): void {
console.log($event, item);
const x = ($event.x ? $event.x : 0);
const y = ($event.y ? $event.y : 0);
const point = new Point(x, y);
this.contextMenuService.show.next({
// Optional - if unspecified, all context menu components will open
contextMenu: this.contextMenu,
event: $event,
item: point,
});
$event.preventDefault();
$event.stopPropagation();
}
I need to add custom context menus for some components In my app. I woud like to use ComponentFactory for this. How can I add menu items in my dynamically added ContextMenuComponent? Please see the below code for reference