Angular 2 : Display different "toolbar" components depending on main navigation

标签 angular angular2-routing angular2-template

这是一个关于如何使用 Angular 2 以“正确”方式实现所需功能的概念性问题。

我的应用程序有一个导航菜单、一个工具栏和一个内容区域。后者包含主要的 <router-outlet>并显示不同的 View ,如列表和详细信息。

我想要实现的是工具栏显示不同的组件,具体取决于内容区域中呈现的组件/ View 。例如,列表组件需要工具栏中的搜索控件,而详细信息组件需要保存按钮。

A) 我的第一次尝试是添加另一个(命名)<router-outlet>到工具栏,根据静态路由显示工具栏组件。这有什么不对的地方:

  1. 静态内容-工具栏关系对我来说太松散了。
  2. 该关系在 URL 中可见(且可更改)。
  3. 即使用户导航离开,工具栏导出也会保留此路径。

B) 我的第二次尝试是在主视图组件的 ngOnInit 中强制导航到工具栏组件(也使用命名的工具栏导出)。 ,它耦合得更紧密。什么味道不好:

  1. A2
  2. A3,为防止这种情况,我可以“清除”ngOnDestroy 上的工具栏 socket ,但我还没有找到方法。

C) 给路由器最后一次机会,因为我发现这种方法有效:

const ROUTES: Routes = [
    {path: "buildings", children: [
        {path: "", component: BuildingListComponent, pathMatch: "full", outlet: "primary"},
        {path: "", component: BuildingListToolbarComponent, pathMatch: "full", outlet: "toolbar"},
        {path: ":id", component: BuildingDashboardComponent, outlet: "primary"}
    ]}
];

想法是路由器会选择匹配路径每个导出。但是(不,它本来可以如此简单)不幸的是,这不起作用:

const ROUTES: Routes = [
    {path: "buildings", children: [
        {path: "list", component: BuildingListComponent, pathMatch: "full", outlet: "primary"},
        {path: "list", component: BuildingListToolbarComponent, pathMatch: "full", outlet: "toolbar"},
        {path: ":id", component: BuildingDashboardComponent, outlet: "primary"}
    ]}
];

它似乎(也可能是意外地)只适用于空路径。为什么,为什么?

D) 一个完全不同的策略是重新设计我的组件层次结构,以便每个主视图组件都包含一个适当的工具栏,并使用 multi-slot content projection .还没有尝试过,但我担心工具栏的多个实例会遇到问题。

有时候,这似乎是一个常见的用例,我想知道 Angular 2 专家将如何解决这个问题。有什么想法吗?

最佳答案

按照 Günter Zöchbauer(谢谢!)的建议,我最终向工具栏添加和删除了动态组件。所需的工具栏组件在路由的数据属性中指定,并由包含工具栏的中央组件(导航栏)评估。
请注意,导航栏组件不需要了解工具栏组件(在功能模块中定义)的任何信息。
希望这对某人有帮助。

buildings-routing.module.ts

const ROUTES: Routes = [
    {path: "buildings", children: [
        {
            path: "",
            component: BuildingListComponent,
            pathMatch: "full",
            data: {toolbar: BuildingListToolbarComponent}
        },
        {
            path: ":id",
            component: BuildingDashboardComponent,
            data: {toolbar: BuildingDashboardToolbarComponent}
        }
    ]}
];

@NgModule({
    imports: [
        RouterModule.forChild(ROUTES)
    ],
    exports: [
        RouterModule
    ]
})
export class BuildingsRoutingModule {
}

navbar.component.html

<div class="navbar navbar-default navbar-static-top">
    <div class="container-fluid">
        <form class="navbar-form navbar-right">
            <div #toolbarTarget></div>
        </form>
    </div>
</div>

navbar.component.ts

@Component({
    selector: 'navbar',
    templateUrl: './navbar.component.html',
    styleUrls: ['./navbar.component.scss']
})
export class NavbarComponent implements OnInit, OnDestroy {

    @ViewChild("toolbarTarget", {read: ViewContainerRef})
    toolbarTarget: ViewContainerRef;

    toolbarComponents: ComponentRef<Component>[] = new Array<ComponentRef<Component>>();
    routerEventSubscription: ISubscription;


    constructor(private router: Router,
                private componentFactoryResolver: ComponentFactoryResolver) {
    }

    ngOnInit(): void {
        this.routerEventSubscription = this.router.events.subscribe(
            (event: Event) => {
                if (event instanceof NavigationEnd) {
                    this.updateToolbarContent(this.router.routerState.snapshot.root);
                }
            }
        );
    }

    ngOnDestroy(): void {
        this.routerEventSubscription.unsubscribe();
    }

    private updateToolbarContent(snapshot: ActivatedRouteSnapshot): void {
        this.clearToolbar();
        let toolbar: any = (snapshot.data as {toolbar: Type<Component>}).toolbar;
        if (toolbar instanceof Type) {
            let factory: ComponentFactory<Component> = this.componentFactoryResolver.resolveComponentFactory(toolbar);
            let componentRef: ComponentRef<Component> = this.toolbarTarget.createComponent(factory);
            this.toolbarComponents.push(componentRef);
        }
        for (let childSnapshot of snapshot.children) {
            this.updateToolbarContent(childSnapshot);
        }
    }

    private clearToolbar() {
        this.toolbarTarget.clear();
        for (let toolbarComponent of this.toolbarComponents) {
            toolbarComponent.destroy();
        }
    }
}

引用资料:
https://vsavkin.com/angular-router-understanding-router-state-7b5b95a12eab
https://engineering-game-dev.com/2016/08/19/angular-2-dynamically-injecting-components
Angular 2 dynamic tabs with user-click chosen components
Changing the page title using the Angular 2 new router

关于 Angular 2 : Display different "toolbar" components depending on main navigation,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41036566/

相关文章:

angular - 无法绑定(bind)到 'routerLink',因为它不是已知属性

Angular 2 *ngFor 语法

javascript - 在 Angular2 中添加外部库

javascript - 单击 Angular 时更改垫子按钮的颜色

angular - 从前端访问azure databricks Rest api时出现CORS错误

angular - Ionic-Framework (4) - Openlayers map 不工作/可见

angular - 使用模板插值多次调用函数?

当只有 1 个 http 请求时,Angular HttpClient 发出多个 http 请求

angular - 如何根据 Angular 2 中的条件重定向到特定路由。

javascript - Angular 2 中的哈希定位策略