Hier ist, was ich denke, ist ein guter Weg, es zu tun.
Sie benötigen 1 Service, 1 Komponente und 1 Direktive.
Here is a plunker
Erläuterung:
Der Service ContextMenuService
:
- ein Thema vom Typ bietet
{event:MouseEvent,obj:any[]}
durch ContextMenuHolderComponent
abonniert zu sein und Werte zu erhalten von ContextMenuDirective
Code:
import {Injectable} from 'angular2/core';
import {Subject} from 'rxjs/Rx';
@Injectable()
export class ContextMenuService{
public show:Subject<{event:MouseEvent,obj:any[]}> = new Subject<{event:MouseEvent,obj:any[]}>();
}
und fügen Sie es in die Liste der Anbieter in bootstrap()
bootstrap(AppComponent,[ContextMenuService]);
Die Komponente ContextMenuHolderComponent
:
- Diese c omponent wird innerhalb der Root-Komponente hinzugefügt. z.B.
AppComponent
und es hat eine fixed
Position.
Es schließt sich der subject
in ContextMenuService
zu erhalten:
- Menüpunkte vom Typ
{title:string,subject:Subject}[]
wird das Objekt verwendet, um die angeklickt Wert innerhalb des Menüs zu senden
- Mouseevent-Objekt
Es verfügt über einen Ereignis-Listener (document:click)
, um das Menü bei Klicks außerhalb des Menüs zu schließen.
Code:
@Component({
selector:'context-menu-holder',
styles:[
'.container{width:150px;background-color:#eee}',
'.link{}','.link:hover{background-color:#abc}',
'ul{margin:0px;padding:0px;list-style-type: none}'
],
host:{
'(document:click)':'clickedOutside()'
},
template:
`<div [ngStyle]="locationCss" class="container">
<ul>
<li (click)="link.subject.next(link.title)" class="link" *ngFor="#link of links">
{{link.title}}
</li>
</ul>
</div>
`
})
class ContextMenuHolderComponent{
links = [];
isShown = false;
private mouseLocation :{left:number,top:number} = {left:0;top:0};
constructor(private _contextMenuService:ContextMenuService){
_contextMenuService.show.subscribe(e => this.showMenu(e.event,e.obj));
}
// the css for the container div
get locationCss(){
return {
'position':'fixed',
'display':this.isShown ? 'block':'none',
left:this.mouseLocation.left + 'px',
top:this.mouseLocation.top + 'px',
};
}
clickedOutside(){
this.isShown= false; // hide the menu
}
// show the menu and set the location of the mouse
showMenu(event,links){
this.isShown = true;
this.links = links;
this.mouseLocation = {
left:event.clientX,
top:event.clientY
}
}
}
Und es um die Wurzelkomponente hinzuzufügen:
@Component({
selector: 'my-app',
directives:[ContextMenuHolderComponent,ChildComponent],
template: `
<context-menu-holder></context-menu-holder>
<div>Whatever contents</div>
<child-component></child-component>
`
})
export class AppComponent { }
Die letzte, ContextMenuDirective
:
- Es fügt dem Host-Element ein
contextmenu
-Ereignis hinzu.
- Übernehmen Sie eine Eingabe einer Liste von Elementen, die an
ContextMenuHolderComponent
übergeben werden sollen.
Code:
@Directive({
selector:'[context-menu]',
host:{'(contextmenu)':'rightClicked($event)'}
})
class ContextMenuDirective{
@Input('context-menu') links;
constructor(private _contextMenuService:ContextMenuService){
}
rightClicked(event:MouseEvent){
this._contextMenuService.show.next({event:event,obj:this.links});
event.preventDefault(); // to prevent the browser contextmenu
}
}
Das ist es. Alles, was Sie jetzt tun müssen, ist die Anweisung an ein Element anzuhängen und es an eine Liste von Elementen zu binden. Zum Beispiel:
@Component({
selector:'child-component',
directives:[ContextMenuDirective],
template:`
<div [context-menu]="links" >right click here ... {{firstRightClick}}</div>
<div [context-menu]="anotherLinks">Also right click here...{{secondRightClick}}</div>
`
})
class ChildComponent{
firstRightClick; secondRightClick;
links;
anotherLinks;
constructor(){
this.links = [
{title:'a',subject:new Subject()},
{title:'b',subject:new Subject()},
{title:'b',subject:new Subject()}
];
this.anotherLinks = [
{title:'link 1',subject:new Subject()},
{title:'link 2',subject:new Subject()},
{title:'link 3',subject:new Subject()}
];
}
// subscribe to subjects
ngOnInit(){
this.links.forEach(l => l.subject.subscribe(val=> this.firstCallback(val)));
this.anotherLinks.forEach(l => l.subject.subscribe(val=> this.secondCallback(val)))
}
firstCallback(val){
this.firstRightClick = val;
}
secondCallback(val){
this.secondRightClick = val;
}
}
Ist es wirklich wichtig, wo Sie die Komponente einfügen? Sie können es einfach absolut positionieren. Ich würde der 'AppComponent' eine Kontext-Menü-Komponente hinzufügen und Anweisungen senden, was sie mit einem gemeinsam genutzten globalen Service anzeigen soll. Hete ist ein Beispiel, in dem HTML dem Körper, aber nicht den Komponenten dynamisch hinzugefügt wird. AFAIK https://github.com/angular/angular/blob/master/modules/angular2_material/src/components/dialog/dialog.ts –
Es ist wichtig wenn der Elternteil "overflow: hidden" hat, was viele tun. –
Ich habe tatsächlich mit "position: fixed" aufgewickelt, was den "overflow: hidden" der Eltern zu ignorieren scheint. Es fühlt sich ein wenig dreckig an, aber es macht den Job erledigt. –