2016-08-18 4 views
0

Ich spiele gerade mit Angular 2. Momentan frage ich mich, was der beste Weg ist, um eine dynamische Menükomponente zu implementieren. Die Idee ist, eine globale Menükomponente zu haben, die Menüelemente für jede andere Inhaltskomponente in der App empfängt.Angular 2 Dynamic Menü

Eine Idee war, eine menu.component, einen menu.service und andere Komponenten zu haben, die den Service nutzen. Der Service wird global in app.module referenziert. Die menu.components abonniert eine beobachtbare und fügt dynamisch Elemente hinzu, die eingefügt werden. Ich denke, das ist nicht die beste Lösung.

Eine andere Idee ist, dass ich den Menü-Selektor/Tag in jede Komponentenvorlage einfügen und Daten direkt in die Menükomponente einfügen. Zur Zeit habe ich festgestellt, wie man Elemente in die menu.component bringt, sodass die ngFor die Menüelemente nach der Initialisierung generiert.

Die Frage ist also: Gibt es irgendwelche "Best Practices" oder gebräuchlichen Weg, dies zu tun?

Ich denke, ich brauche ein paar Grundlagen in Angular 2, aber es wäre schön, wenn Sie mich auf dem richtigen Weg führen können. Es ist nur ein Lernprojekt :)

Hier ist ein Code von dem, was ich versucht habe:

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

„Dynamic erstellen Menü "ist ziemlich generisch. Sie haben keine Anforderungen für das Menü angegeben. In dieser Frage geht es auch um Meinungen, die von SO abgeraten werden. –

+0

OK - die Idee ist, dass es eine getrennte Menükomponente gibt und jede "Inhalts" -Komponente eigene Menüelemente hat, die in diese Menükomponente übertragen werden. Klingt wirklich einfach. Ich denke, das ist ein häufiger Anwendungsfall. –

+0

Bearbeiten Sie Ihre Frage und veröffentlichen Sie den Code, der zeigt, was Sie versuchen zu erreichen und wo Sie fehlgeschlagen sind. –

Antwort

1

Das erste, was mir nach dem Lesen dieser Frage in den Sinn kommt, ist routes. Sie können verschachtelte Routen verwenden, um ein solches Menü zu erstellen. Nach dem erneuten Laden der Seite befinden Sie sich auf der gleichen Seite. Zusätzlich können Sie verschiedene Menüpunkte basierend auf der aktuellen Route generieren, wenn Sie solche Funktionen benötigen.

routes documentation

Für Routen dynamisch ändern können Sie resetConfig Methode verwenden. resetConfig

für Stammkomponente Verwendung prefix Art von pathMatch

Der anderen möglichen pathMatch Wert ist ‚prefix‘, die den Router erzählt die Umleitungsroute entsprechen, wenn die verbleibende URL mit der Präfix Pfad der Umleitung Route beginnt.


Zum Synchronisieren von Daten zwischen Komponenten Service verwenden, die von Haupt app Datei vorgesehen ist.


Wenn Sie dynamischeren Ansatz benötigen dann Compiler benutzen, um Ihre Komponenten zu der Ansicht zu injizieren. 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); 
    }); 
6

Ich habe genau das umgesetzt, was Sie in meinem Projekt möchte. Hier ist, wie ich es tat:

1- ich einen Dienst erstellt, die ich ContextMenuService genannt, die für die Instanziierung des Kontextmenüs verantwortlich ist

2- ich eine ContextMenu Komponente erstellt, die das tatsächliche Menü angezeigt werden.

3- Der Dienst erstellt die Komponente dynamisch, und bei einem Klick außerhalb des Menüs zerstört der Dienst die Menükomponente.

Hier ist der Service:

@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 
) { } 
} 

Jetzt habe ich diesen Service in das App-Modul injizieren, den Provider-Array verwendet wird, sondern auch, ich brauche ContextMenuComponent zu App-Modul in der Liste der entryComponents zur Verfügung zu stellen, entryComponents ist eine Liste für alle Komponenten, die dynamisch erstellt werden:

@NgModule({ 
    declarations: [ 
    AppComponent, 
    ContextMenuComponent 
    ], 
    providers: [ 
    ContextMenuService, 
    ], 
    bootstrap: [AppComponent], 
    entryComponents: [ContextMenuComponent] 
}) 
export class AppModule{} 

schließlich in dem Konstruktor der wichtigsten app Komponente, ich sage, dass der ViewContainerRef des Kontextmenü-Service, der Schiedsrichter der App Ansicht Container Komponente:

@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; 
    } 
} 

Mein ContextMenuComponent sieht einfach wie folgt aus:

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(); 
    } 
    } 
} 

Mit dieser Vorlage:

<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> 

Ich hoffe, dies könnte Ihnen helfen Ihre eigenen Kontextmenü