我正在玩Angular 2。 目前我想知道实现动态菜单组件的最佳方法是什么。 我们的想法是拥有一个全局菜单组件,用于接收应用程序中每个其他内容组件的菜单项。
有一个想法是拥有一个menu.component,一个menu.service和其他使用该服务的组件。该服务在app.module中全局引用。 menu.components订阅了一个observable并动态添加了粘贴的项目。我认为这不是最好的解决方案。
另一个想法是,我将菜单选择器/标签放在每个组件模板中,并将数据直接粘贴到菜单组件。目前我坚持如何将项目输入menu.component,以便ngFor在初始化后生成菜单项。
所以问题是:是否有“最佳做法”或常用方法?
猜猜我在Angular 2中需要更多基础知识,但如果能引导我走上正确的道路,那就太好了。这只是一个学习项目:))
以下是我尝试过的一些代码:
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HTTP_PROVIDERS } from '@angular/http';
import { AppComponent } from './app.component';
import { appRouterProviders } from './app.routes';
import { MenuComponent } from './menu.component';
import { HomeComponent } from './home.component';
@NgModule({
imports: [BrowserModule],
declarations:
[
AppComponent
],
bootstrap: [AppComponent],
providers:
[
MenuComponent,
appRouterProviders,
HTTP_PROVIDERS
]
})
export class AppModule { }
menu.component.ts
import { Component } from '@angular/core';
import { ROUTER_DIRECTIVES } from '@angular/router';
export class MenuItem {
name: string;
path: string;
}
@Component({
selector: 'component-menu',
template: `
<ul>
<li *ngFor="let item of menuItems"><a [routerLink]="[item.path]">{{item.name}}</a></li>
</ul>
`
})
export class MenuComponent {
public menuItems: Array<MenuItem>;
constructor() {
}
}
home.component.ts
import { Component, OnInit } from '@angular/core';
import { MenuComponent, MenuItem } from './menu.component';
@Component({
selector: 'app-home',
directives: [],
template: `<h2>Home Component</h2>
<component-menu></component-menu>`
})
export class HomeComponent implements OnInit{
private menuItems:Array<MenuItem>;
constructor(private menuComponent:MenuComponent) {
console.log('Home component ctor');
this.menuItems = [
{name:'Home', path: '/home'},
{name:'Content', path: '/content'}
];
this.menuComponent.menuItems = this.menuItems;
}
ngOnInit(){
}
}
答案 0 :(得分:6)
我已经在我的项目中实现了你想要的。以下是我的表现方式:
1-我创建了一个名为ContextMenuService的服务,负责实例化上下文菜单
2-我创建了一个ContextMenu组件,它是要显示的实际菜单。
3-服务动态创建组件,只要菜单外有单击,服务就会破坏菜单组件。
这是服务:
@Injectable()
export class ContextMenuService {
private _menuAlreadyOn: boolean = false;
private _currentContextMenu: ComponentRef<any>;
viewContainerRef: ViewContainerRef
showContextMenu(event: MouseEvent, options: ContextMenuOption[]) : boolean {
event.stopPropagation();
if (this._menuAlreadyOn) {
this._currentContextMenu.destroy();
this._menuAlreadyOn = false;
}
let componentRef = this.viewContainerRef.createComponent(this._cfr.resolveComponentFactory(ContextMenuComponent))
componentRef.instance.options = options;
componentRef.location.nativeElement.getElementsByTagName('div')[0].style.left = event.clientX
componentRef.location.nativeElement.getElementsByTagName('div')[0].style.top = event.clientY
this._currentContextMenu = componentRef;
this._menuAlreadyOn = true;
let listener = this._eventManager.addGlobalEventListener('document', 'click', () => {
this._currentContextMenu.destroy();
this._menuAlreadyOn = false;
listener();
})
return false;
}
constructor(
private _cfr: ComponentFactoryResolver,
private _eventManager: EventManager
) { }
}
现在,我必须使用providers数组将此服务注入app模块,但是,我还需要在entryComponents列表中为app模块提供ContextMenuComponent,entryComponents是动态创建的所有组件的列表:
@NgModule({
declarations: [
AppComponent,
ContextMenuComponent
],
providers: [
ContextMenuService,
],
bootstrap: [AppComponent],
entryComponents: [ContextMenuComponent]
})
export class AppModule{}
最后,在主app组件的构造函数中,我告诉它上下文菜单服务的ViewContainerRef是app组件的视图容器引用:
@Component ({
selector: 'saugo-viewer',
templateUrl: 'app/views/app.component.html',
styleUrls: ['app/css/app.component.css'],
}
)
export class AppComponent {
constructor(
private contextMenuService: ContextMenuService,
viewContainer: ViewContainerRef
) {
contextMenuService.viewContainerRef = viewContainer;
}
}
我的ContextMenuComponent看起来像这样:
export interface ContextMenuOption {
text: string;
action?: () => void;
icon?: string;
disabled?: boolean;
}
@Component({
selector: 'context-menu',
templateUrl: 'app/views/helpers/context-menu.service.html',
styleUrls: ['app/css/helpers/context-menu.service.css']
})
export class ContextMenuComponent {
options: ContextMenuOption[] = [];
itemClicked(i: number) {
if (this.options[i].action) {
this.options[i].action();
}
}
}
使用此模板:
<div id="context-menu"class="custom-menu">
<ul>
<li *ngFor="let option of options; let i = index"
[ngClass]="{'active-item': !option.disabled}" (click)="itemClicked(i)">
<span [class]="'glyphicon ' + option.icon"></span>
<a href="#" [ngClass]="{'disabled-text': option.disabled}">{{option.text}}</a>
</li>
</ul>
</div>
我希望这可以帮助您创建自己的自定义上下文菜单
答案 1 :(得分:1)
在阅读这个问题后,我首先想到的是routes
。
您可以使用嵌套路径来制作这样的菜单。重新加载页面后,您将在同一页面中。
此外,如果您需要此功能,您可以根据当前路线生成不同的菜单项。
要动态更改路线,您可以使用resetConfig
方法。
resetConfig
父组件使用prefix
类型pathMatch
另一个可能的pathMatch值是'prefix',当剩余的URL以重定向路由的前缀路径开始时,它告诉路由器匹配重定向路由。
要在组件之间同步数据,请使用主应用程序文件提供的服务。
如果您需要更多动态方法,请使用Compiler
将组件注入视图。
Compiler documentation
constructor(
private compiler:Compiler,
private viewContainerRef:ViewContainerRef){}
...
this.compiler.compileComponentAsync(yourComponentClass).then((cmpFactory)=>{
const injector = this.viewContainerRef.injector;
this.viewContainerRef.createComponent(cmpFactory, 0, injector);
});