Angular4:通过路由器的`data` 属性传递面包屑视图组件 [英] Angular4: passing breadcrumb view components through the router's `data` property

查看:47
本文介绍了Angular4:通过路由器的`data` 属性传递面包屑视图组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的 Angular (v4) 应用程序中,我正在尝试创建一个面包屑模块​​.我找到了这个 S.O.问题很有帮助,建议我将面包屑信息存储在路由器的data属性中

In my Angular (v4) app, I'm trying to create a breadcrumb module. I found this S.O. question helpful, suggesting I store breadcrumb information in the router's data property like so

{ path: '', component: HomeComponent, data: {breadcrumb: 'home'}}

但我想做的是在 data 属性中存储一个组件,然后让面包屑模块​​提取并呈现它.允许我像这样与面包屑模块​​交互

But what I'd like to do is store a component in the data property, and then have the breadcrumb module extract and render it. Allowing me to interact with the breadcrumb module like this

{ path: '', component: HomeComponent, data: {breadcrumb: CustomViewComponent}}

遵循 angular 动态组件加载器指南有用的博客文章,我试图让我的面包屑渲染组件执行此操作:

Following the angular Dynamic Component Loader guide and a helpful blog post, I've attempted to make my breadcrumb rendering component do this:

import { Component, Input, OnInit, AfterViewInit,
  ViewChild, ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
import 'rxjs/add/operator/filter';

import { MainLogoComponent } from '../my-material/main-logo/main-logo.component';

@Component({
  selector: 'app-breadcrumbs',
  template: `<div #parent></div>`,
  styleUrls: ['./breadcrumbs.component.scss']
})    
export class BreadcrumbsComponent implements OnInit, AfterViewInit {
  breadcrumbs: Array<any>;
  @ViewChild('parent', {read: ViewContainerRef}) parent: ViewContainerRef;

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

  ngOnInit() {
    this.router.events
      .filter(event => event instanceof NavigationEnd)
      .subscribe(event => {  // note, we don't use event
        this.breadcrumbs = [];
        let currentRoute = this.route.root,
            url = '';
        do {
          let childrenRoutes = currentRoute.children;
          currentRoute = null;
          childrenRoutes.forEach(route => {
            if(route.outlet === 'primary') {
              let routeSnapshot = route.snapshot;
              url += '/' + routeSnapshot.url.map(segment => segment.path).join('/');
              this.breadcrumbs.push({ 
                label: route.snapshot.data.breadcrumb,
                url:   url });
              currentRoute = route;
            }
          })
        } while(currentRoute);
      })
  }

  ngAfterViewInit() {
    const logoComponent = this.componentFactoryResolver
      .resolveComponentFactory(this.breadcrumbs[0]); // at the moment just using breadcrumbs[0] for simplicity
    this.parent.createComponent(logoComponent);
  }
}

不幸的是,路由数据属性似乎不能/不会存储组件.深入调试控制台,数据属性被保存为 {data: {breadcrumb: }}

Unfortunately, it appears that the route data property cannot / will not store a component. Delving into the debug console, the data property gets saved as {data: {breadcrumb: }}

如果有人知道使此代码工作的方法,或者对实现我构建使用其他视图组件的面包屑组件的目标的更好方法有建议,我会非常 感激不尽!!

If anyone knows of a way to make this code work, or has a suggestion for a better way to achieve my goal of building a breadcrumbs component that consumes other view components, I would greatly appreciate it!!

PS:如果我跳过 data 属性,而只是手动要求面包屑组件将 MainLogoComponent 添加到页面,如下所示:

PS: If I skip the data property, and just manually ask the breadcrumb component to add a MainLogoComponent to the page like so:

ngAfterViewInit() {
    const logoComponent = this.componentFactoryResolver.resolveComponentFactory(MainLogoComponent);
    this.parent.createComponent(logoComponent);
  }

有效

为了完整起见,还有相关路由的代码.

And, for completeness, the code for the associated route.

const routes: Routes = [
  {
    path: 'calendar',
    data: {
      breadcrumb: MainLogoComponent
    },
    canActivate: [ AuthGuard ],
    children: [
      {
        path: '',
        component: CalendarComponent
      }
    ]
  }
];

更新

这是相关的 NgModule:

Here is the relevant NgModule:

@NgModule({
  imports: [
    CommonModule,
    MyMaterialModule
  ],
  exports: [
    BreadcrumbsComponent
  ],
  entryComponents: [BreadcrumbsComponent, MainLogoComponent],
  declarations: [BreadcrumbsComponent] // MainLogoComponent is declared in MyMaterialModule
})
export class BreadcrumbsModule { }

推荐答案

所以我让它如我所愿地工作(非常感谢@yurzui 花时间创建一个plnkr 展示了它在概念上应该可以工作!!!!!!这真的真的帮助了我,我真是太棒了!!).我仍然不确定我做错了什么,但我创建了一个新应用程序来制作面包屑模块​​,让它工作,然后将它移植到我的真实应用程序中.我发现一个可能的问题是 ng serve 似乎偶尔会错过重新编译我已更改的文件(或者我的浏览器可能正在缓存错误).现在有几次,编译器吐出关于我已删除的变量的错误,当我重新启动 ng serve(不更改任何内容)时,错误消失了.给其他人解决问题的警告(尽管它可能与我的开发设置无关).

So I got it working as I wanted it to work (a BIG thank you to @yurzui for taking the time to create a plnkr showing it should work, in concept!!!!! That REALLY REALLY helped and I'm incredibly greatful!!). I'm still not exactly sure where I was doing wrong, but I created a new app to make the breadcrumbs module, got it working, and then ported it to my real app. One probable issue, I've discovered, is that ng serve seems to occationally miss recompiling files I've changed (or perhaps my browser is caching errors). Several times now, the compiler has spit out errors concerning variables I've deleted and when I restart ng serve (without changing anything) the errors go away. A warning for other people troubleshooting problems (though maybe it's isolated to my dev setup).

这是最终的、有效的面包屑代码(注意,如果您想复制我的方法,请阅读原始问题中的信息/链接).

Here is the final, working breadcrumbs code (note, if you're trying to replicate my method also read through the info / links in the original question).

import { Component, AfterViewInit, ViewChild, ComponentFactoryResolver, ViewContainerRef } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import 'rxjs/add/operator/filter';

import { BreadDividerComponent } from './bread-divider/bread-divider.component';

@Component({
  selector: 'app-breadcrumbs',
  template: `
    <div #breadContainer></div>
    <div #iconContainer></div>
    <div #lastBreadContainer></div>`
})
export class BreadcrumbsComponent implements AfterViewInit {
  breadcrumbs: Array<any>;
  storedBreadcrumbs: Array<Component>;
  @ViewChild('breadContainer', {read: ViewContainerRef}) breadContainer: ViewContainerRef;
  @ViewChild('iconContainer', {read: ViewContainerRef}) iconContainer: ViewContainerRef;
  @ViewChild('lastBreadContainer', {read: ViewContainerRef}) lastBreadContainer: ViewContainerRef;

  constructor(private componentFactoryResolver: ComponentFactoryResolver,
    private router: Router) { }
  
  ngAfterViewInit() {
    this.router.events
      .filter(event => event instanceof NavigationEnd)
      .subscribe(event => {
        // Reset variables and clear breadcrumb component on view change
        this.breadcrumbs = [];
        this.storedBreadcrumbs = [];
        this.breadContainer.clear();
        this.iconContainer.clear();
        this.lastBreadContainer.clear();

        // Drill down through child routes
        let currentRoute = this.router.routerState.snapshot.root.firstChild;
        if (currentRoute) {
          do {
            this.breadcrumbs.push(currentRoute.data.breadcrumb);
            currentRoute = currentRoute.firstChild;
          } while (currentRoute)
        }

        // Because each route's home path is actually itself a child route
        // e.g. { path: 'two', children: [ { path: '', component: ... } ] }
        if (this.breadcrumbs.length === 2 
          && this.breadcrumbs[0] === this.breadcrumbs[1]) {
            this.breadcrumbs = [this.breadcrumbs[0]];
        }

        this.breadcrumbs.forEach((breadcrumb, index, array) => {
          if (array.length > 1) {
            if (index < array.length - 1) {
              const breadFactory = this.componentFactoryResolver
                .resolveComponentFactory(breadcrumb);
              this.storedBreadcrumbs.push(this.breadContainer.createComponent(breadFactory));

              const iconFactory = this.componentFactoryResolver
                .resolveComponentFactory(BreadDividerComponent);
              this.storedBreadcrumbs.push(this.iconContainer.createComponent(iconFactory));
            } else {
              const breadFactory = this.componentFactoryResolver
                .resolveComponentFactory(breadcrumb);
              this.storedBreadcrumbs.push(this.lastBreadContainer.createComponent(breadFactory));
            }
          } else {
            const breadFactory = this.componentFactoryResolver
              .resolveComponentFactory(breadcrumb);
            this.storedBreadcrumbs.push(this.lastBreadContainer.createComponent(breadFactory));
          }
        });
      });
  }
}

面包屑视图组件通过路由器的数据属性传递,如下所示:

Breadcrumb view components are passed through the router's data property, like so:

const routes: Routes = [
  {
    path: 'calendar',
    data: {
      breadcrumb: CalendarBreadcrumbComponent,
      menu: CalendarMenuComponent
    },
    canActivate: [ AuthGuard ],
    children: [
      {
        path: 'events',
        data: {
          breadcrumb: EventsBreadcrumbComponent
        },
        component: EventsComponent
      },
      {
        path: '',
        pathMatch: 'full',
        redirectTo: 'events'
      }
    ]
  }
];

我也使用这种策略来构建我的应用菜单.这是面包屑视图组件的示例

I also use this strategy to build my app's menu. Here's an example of a breadcrumb view component

import { Component, HostBinding } from '@angular/core';
import { DomSanitizer  } from '@angular/platform-browser';

@Component({
  selector: 'app-calendar-breadcrumb',
  templateUrl: './calendar-breadcrumb.component.html',
  styleUrls: ['./calendar-breadcrumb.component.scss']
})
export class CalendarBreadcrumbComponent {
  // Modify this components parent element so that it is displayed with flexbox
  @HostBinding('attr.style') style = this.sanitizer
    .bypassSecurityTrustStyle('display: flex; flex-direction: row;');

  constructor(private sanitizer: DomSanitizer) {}
}

以及相关的视图文件(我现在意识到,它真的不需要自己的文件...)

And the associated view file (which, I'm realizing now, really doesn't need its own file...)

<md-icon>event</md-icon><h3>Calendar</h3>

这篇关于Angular4:通过路由器的`data` 属性传递面包屑视图组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆