在我的Angular(v4)应用程序中,我正在尝试创建一个breadcrumb模块。我发现this S.O. question有帮助,建议我将痕迹信息存储在路由器的data
属性中,如此
{ path: '', component: HomeComponent, data: {breadcrumb: 'home'}}
但我想要做的是在数据属性中存储组件,然后让breadcrumb模块解压缩并呈现它。允许我像这样与面包屑模块进行交互
{ path: '', component: HomeComponent, data: {breadcrumb: CustomViewComponent}}
在角度Dynamic Component Loader guide和helpful blog post之后,我试图让我的面包屑渲染组件执行此操作:
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属性将保存为{data: {breadcrumb: }}
如果有人知道如何使这段代码有效,或者有更好的方法来实现构建消耗其他视图组件的breadcrumbs组件的目标,那么我 非常 欣赏它!!
PS:如果我跳过data
属性,只需手动要求breadcrumb组件向页面添加MainLogoComponent
,如下所示:
ngAfterViewInit() {
const logoComponent = this.componentFactoryResolver.resolveComponentFactory(MainLogoComponent);
this.parent.createComponent(logoComponent);
}
它有效
并且,为了完整性,相关路线的代码。
const routes: Routes = [
{
path: 'calendar',
data: {
breadcrumb: MainLogoComponent
},
canActivate: [ AuthGuard ],
children: [
{
path: '',
component: CalendarComponent
}
]
}
];
更新
以下是相关的NgModule:
@NgModule({
imports: [
CommonModule,
MyMaterialModule
],
exports: [
BreadcrumbsComponent
],
entryComponents: [BreadcrumbsComponent, MainLogoComponent],
declarations: [BreadcrumbsComponent] // MainLogoComponent is declared in MyMaterialModule
})
export class BreadcrumbsModule { }
答案 0 :(得分:1)
所以我得到它的工作,因为我希望它工作(非常感谢@yurzui 花时间创建一个plnkr显示它应该工作,在概念!!!!!那真的 真的 有帮助,我非常高兴!!)。我仍然不确定我做错了什么,但我创建了一个新的应用程序来制作breadcrumbs模块,让它工作,然后将它移植到我的真实应用程序。我发现,一个可能的问题是ng serve
似乎错过了重新编译我已经改变的文件(或者我的浏览器可能是缓存错误)。好几次,编译器吐出了有关我删除的变量的错误,当我重新启动ng serve
(没有改变任何东西)时,错误消失了。警告其他人解决问题(虽然它可能与我的开发设置隔离)。
这是最终的,正在运行的 breadcrumbs 代码(注意,如果您尝试复制我的方法,也请阅读原始问题中的信息/链接)。
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视图组件通过路由器的data属性传递,如下所示:
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'
}
]
}
];
我也使用这种策略来构建我的应用程序菜单。这是一个面包屑视图组件的示例
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) {}
}
相关的视图文件(我现在意识到,实际上并不需要自己的文件......)
<md-icon>event</md-icon><h3>Calendar</h3>