这是我认为的一个好方法。
您需要 1 个服务、1 个组件和 1 个指令。
这是一个笨蛋 http://plnkr.co/edit/3klGukkbDBCOaBYqcGmr?p=preview
解释:
服务ContextMenuService
:
- 提供一个主题类型
{event:MouseEvent,obj:any[]}
成为
订阅者ContextMenuHolderComponent
,并接收值
从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[]}>();
}
并将其添加到提供者列表中bootstrap()
bootstrap(AppComponent,[ContextMenuService]);
组件ContextMenuHolderComponent
:
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
}
}
}
并将其添加到根组件中:
@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 { }
最后一个,ContextMenuDirective
:
- 它添加了一个
contextmenu
事件到宿主元素。
- 接受要传递到的项目列表的输入
ContextMenuHolderComponent
.
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
}
}
就是这样。您现在需要做的就是附加[context-menu]
指令到一个元素并将其绑定到一个项目列表。例如:
@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;
}
}