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

查看:46
本文介绍了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}}

遵循有角度的《动态组件加载程序》指南有用的博客文章,我试图制作我的面包屑渲染组件可以做到这一点:

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

不幸的是,似乎route data属性不能/不会存储组件.进入调试控制台,数据属性将另存为{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 { }

推荐答案

所以我按我希望的那样工作了(BIG非常感谢@yurzui抽出宝贵的时间来创建一个plnkr在概念上显示它应该可以正常工作!!!!!真的 REALLY 很有帮助,我非常了不起!).我仍然不确定自己在哪里做错了,但是我创建了一个新应用程序来制作面包屑模块​​,使其正常运行,然后将其移植到我的真实应用程序中.我发现了一个可能的问题,就是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).

这是最后可用的 breadcrumbs 代码(请注意,如果您要复制我的方法,还请阅读原始问题中的info/链接).

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

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

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天全站免登陆