Angular 角度2:显示不同的“;工具栏“;取决于主导航的组件
这是一个关于如何使用Angular 2以“正确”的方式实现所需功能的概念性问题 我的应用程序有一个导航菜单、一个工具栏和一个内容区。后者包含主要的Angular 角度2:显示不同的“;工具栏“;取决于主导航的组件,angular,angular2-routing,angular2-template,Angular,Angular2 Routing,Angular2 Template,这是一个关于如何使用Angular 2以“正确”的方式实现所需功能的概念性问题 我的应用程序有一个导航菜单、一个工具栏和一个内容区。后者包含主要的,并显示不同的视图,如列表和详细信息 我想要实现的是,工具栏显示不同的组件,具体取决于内容区域中呈现的组件/视图。例如,列表组件需要工具栏中的搜索控件,而详细信息组件需要保存按钮 A)我的第一次尝试是将另一个(命名的)添加到工具栏,并基于静态路由显示工具栏组件。这有什么不对的地方: 静态内容工具栏关系的耦合过于松散,不合我的口味 该关系在URL中可见(
,并显示不同的视图,如列表和详细信息
我想要实现的是,工具栏显示不同的组件,具体取决于内容区域中呈现的组件/视图。例如,列表组件需要工具栏中的搜索控件,而详细信息组件需要保存按钮
A)我的第一次尝试是将另一个(命名的)
添加到工具栏,并基于静态路由显示工具栏组件。这有什么不对的地方:
ngOnInit
中强制导航到工具栏组件(也使用命名的工具栏出口),这将使它更紧密地结合在一起。什么气味难闻:
const ROUTES:ROUTES=[
{路径:“建筑物”,儿童:[
{path:,组件:BuildingListComponent,路径匹配:“full”,出口:“primary”},
{路径:,组件:BuildingListToolbarComponent,路径匹配:“完整”,出口:“工具栏”},
{路径:“:id”,组件:BuildingDashboardComponent,出口:“主”}
]}
];
这个想法是路由器将选择每个插座的匹配路径。但是(不,这可能很容易)不幸的是,这不起作用:
const ROUTES:ROUTES=[
{路径:“建筑物”,儿童:[
{路径:“列表”,组件:BuildingListComponent,路径匹配:“完整”,出口:“主”},
{路径:“列表”,组件:BuildingListToolbarComponent,路径匹配:“完整”,出口:“工具栏”},
{路径:“:id”,组件:BuildingDashboardComponent,出口:“主”}
]}
];
它显然(可能是偶然)只在空路径下工作。为什么,为什么
D)一个完全不同的策略是修改我的组件层次结构,以便每个主视图组件都包含一个适当的工具栏,并使用。我还没有尝试过这个,但是我担心工具栏的多个实例会出现问题
有时,这似乎是一个常见的用例,我想知道Angular 2专家会如何解决这个问题。有什么想法吗?正如Günter Zöchbauer(谢谢!)所建议的,我最终在工具栏上添加和删除了动态组件。所需的工具栏组件在路由的数据属性中指定,并由包含工具栏的中心组件(导航栏)进行评估。
请注意,导航栏组件不需要了解工具栏组件的任何信息(这些组件在feauture模块中定义)。
希望这对别人有帮助 建筑物布线.module.ts
const ROUTES:ROUTES=[
{路径:“建筑物”,儿童:[
{
路径:“”,
组件:BuildingListComponent,
路径匹配:“满”,
数据:{工具栏:BuildingListToolbarComponent}
},
{
路径:“:id”,
组件:BuildingDashboardComponent,
数据:{工具栏:BuildingDashboardToolbarComponent}
}
]}
];
@NGD模块({
进口:[
RouterModule.forChild(路由)
],
出口:[
路由模块
]
})
导出类BuildingRoutingModule{
}
navbar.component.html
navbar.component.ts
@组件({
选择器:“导航栏”,
templateUrl:'./navbar.component.html',
样式URL:['./navbar.component.scss']
})
导出类NavbarComponent实现OnInit、OnDestroy{
@ViewChild(“toolbarTarget”,{read:ViewContainerRef})
工具栏目标:ViewContainerRef;
toolbarComponents:ComponentRef[]=新数组();
routerEventSubscription:ISubscription;
构造函数(专用路由器:路由器、,
专用componentFactoryResolver:componentFactoryResolver){
}
ngOnInit():void{
this.routerEventSubscription=this.router.events.subscribe(
(事件:事件)=>{
if(NavigationEnd的事件实例){
this.updateToolbarContent(this.router.routerState.snapshot.root);
}
}
);
}
ngOnDestroy():void{
此.routereventsubscribe.unsubscribe();
}
私有updateToolbarContent(快照:ActivatedRouteSnapshot):无效{
这个.clearToolbar();
让toolbar:any=(snapshot.data为{toolbar:Type});
if(类型的工具栏实例){
让工厂:ComponentFactory=this.componentFactoryResolver.resolveComponentFactory(工具栏);
让componentRef:componentRef=this.toolbarTarget.createComponent(工厂);
这个.toolbarComponents.push(componentRef);
}
for(让snapshot.children的childSnapshot){
此.updateToolbarContent(childSnapshot);
}
}
专用clearToolbar(){
this.toolbarTarget.clear();
for(让toolbarComponents成为此.toolbarComponents的一部分){
toolbarComponent.destroy();
}
}
}
参考文献:我的角度6解决方案 我遇到了一些延迟加载模块的问题,最终通过添加
import { ParamMap } from '@angular/router';
import { SEvent, SService } from '../s-service.service';
import { Observable } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
import { Component, OnInit, OnDestroy, ViewChild, ViewContainerRef, ComponentRef, ComponentFactory, ComponentFactoryResolver, Type } from '@angular/core';
import { SubscriptionLike } from 'rxjs';
import { Router, NavigationEnd, ActivatedRouteSnapshot, ResolveStart, ChildActivationEnd, ActivatedRoute } from '@angular/router';
import { Event } from '@angular/router';
import { filter, take } from 'rxjs/operators';
@Component({
selector: 'app-list',
templateUrl: './s-list.component.html',
styleUrls: ['./s-list.component.scss']
})
export class SListComponent implements OnInit, OnDestroy {
isActive = false;
@ViewChild("toolbarTarget", {read: ViewContainerRef}) toolbarTarget: ViewContainerRef;
toolbarComponents: ComponentRef<Component>[] = new Array<ComponentRef<Component>>();
routerEventSubscription: SubscriptionLike;
seismicEvents: Observable<SEvent[]>;
selectedId: number;
constructor(private service: SService,
private router: Router,
private route: ActivatedRoute,
private componentFactoryResolver: ComponentFactoryResolver) { }
ngOnInit() {
this.sEvents = this.route.paramMap.pipe(
switchMap((params: ParamMap) => {
this.selectedId = +params.get('id');
return this.service.getSEvents();
})
);
// used this on component init to trigger updateToolbarContent
this.updateToolbarContent(this.route.snapshot);
// kept this like above (with minor modification) to trigger subsequent changes
this.routerEventSubscription = this.router.events.pipe(
filter(e => e instanceof ChildActivationEnd),
take(1)
).subscribe(
(event: Event) => {
if (event instanceof ChildActivationEnd) {
this.updateToolbarContent(this.route.snapshot);
}
}
);
}
ngOnDestroy() {
this.routerEventSubscription.unsubscribe();
}
private clearToolbar() {
this.toolbarTarget.clear();
for (let toolbarComponent of this.toolbarComponents) {
toolbarComponent.destroy();
}
}
private updateToolbarContent(snapshot: ActivatedRouteSnapshot) {
// some minor modifications here from above for my use case
this.clearToolbar();
console.log(snapshot);
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);
}
}
}
<app-toolbar></app-toolbar>
<ng-template #toolbaractions>
<button (click)="refresh()">refresh</button>
</ng-template>
export class S3BrowsePageComponent implements AfterViewInit {
@ViewChild('toolbaractions', { read: TemplateRef })
public toolbaractions: TemplateRef<any>;
menu = new BehaviorSubject<TemplateRef<any>>(null);
ngAfterViewInit(): void {
this.menu.next(this.toolbaractions)
}
...
<toolbar>
<ng-container [ngTemplateOutlet]="menu"></ng-container>
</toolbar>
<sidenav>...</sidenav>
<maincontent>
<router-outlet (activate)='onActivate($event)'
(deactivate)='onDeactivate($event)'></router-outlet>
</maincontent>
onActivate($event: any) {
console.log($event);
if ($event.hasOwnProperty('menu')) {
this.menuSubscription = $event['menu']
.subscribe((tr: TemplateRef<any>) => this.menu = tr)
}
}
}
onDeactivate($event: any) {
this.menu = null;
this.menuSubscription.unsubscribe();
}