目前在改进中的博客后台在发生 NavigationError 路由错误时是这样处理的,在 NavigationStart 事件处理函数中先将当前 tab 设置为 active。由于遇到路由错误,RouteOutlet 没有加载当前 tab 对应的 component,RouteOutlet 中依然是前任 tab 的 component,其中包含 sidebar 子组件,所以 sidebar 中显示的依然是前任 tab 对应的内容,下面的 $sideBar.next(undefined!)
就是为了清除 sidebar 中的内容
async ngOnInit() {
this._router.events.subscribe((ev) => {
if (ev instanceof NavigationStart) {
this.setActiveTab(ev.url);
this.spinnerService.toggle(true);
}
else if (ev instanceof NavigationError) {
this.notifyService.error('路由出错:' + ev.error);
this.sideBarSvc.$sideBar.next(undefined!);
this._changeDetector.markForCheck();
}
}
更新 sidebar 是在 app-layout.component.ts 中完成的
ngOnInit() {
this._sideBarSvc.$sideBar.pipe(takeUntil(this._unSub)).subscribe(x => {
this.sidebarVcr?.clear();
if (x) this.sidebarVcr?.createEmbeddedView(x);
this._changeDetectionRef.markForCheck();
});
}
经验证,这个方法清除 sidebar 是可行的,但却带来一个后遗症。
比如当前所在 tab 是「文章」(对应的路径是/articles
),点击「日记」tab(对应的路径是/diaries
),由于路由配置问题造成下面的 NavigationError
NG04014: Invalid configuration of route 'diaries/': routes must have either a path or a matcher specified
这时「日记」tab 出于 active 状态,侧边栏内容被清空,博文列表区块显示的是 spinner,符合预期。
接着点击「文章」tab(出现 NavigationError 之前所在的 tab),问题来了,sidebar 没有更新,还是空白状态。
此时,只要点击其他 tab,比如「随笔」tab (对应的路径是/posts
),sidebar 就能正常更新,然后再点击「文章」tab,sidebar 也正常了。
点击「文章」tab,RouteOutlet 中加载的是 ArticlesMainComponent,删减后的 template 文件内容如下:
<cnb-post-list [postType]="postType"></cnb-post-list>
@defer {
<cnb-article-sidebar></cnb-article-sidebar>
}
我们关心的是 sidebar,所以进一步看 <cnb-article-sidebar>
的 template 文件内容
<ng-template cnbLayoutSidebar>
<cnb-sidebar header="博客后台" [items]="operations"></cnb-sidebar>
<cnb-sidebar header="分类">
<ng-template #sidebarContent>
<cnb-sidebar-blog-categories></cnb-sidebar-blog-categories>
</ng-template>
</cnb-sidebar>
</ng-template>
关键就在上面的 cnbLayoutSidebar
directive,是它触发 sidebar 的更新,下面是它的实现代码
@Directive({
selector: '[cnbLayoutSidebar]',
})
export class LayoutSidebarDirective implements OnInit {
constructor(
private _sideBarSvc: SideBarService,
private sideBar: TemplateRef<any>,
private readonly categoryStore: BlogCategoryStore,
@Inject(BLOG_CATEGORY_TYPE) private categoryType: BlogCategoryType) { }
ngOnInit() {
if (this.sideBar) {
this.categoryStore.refresh(this.categoryType);
this._sideBarSvc.$sideBar.next(this.sideBar);
}
}
}
不知道为什么这个地方在发生 NavigationError 后不能正常更新 sidebar
解决了!问题的原因很简单,在发生 NavigationError 后再点击出错前所在的 tab,没有触发 ngOnInit 事件,所以没有触发 sidebar 更新。
只要在 NavigationEnd router event 发生时触发 sidebar 更新就解决了,改进后的代码如下:
@Directive({
selector: '[cnbLayoutSidebar]',
})
export class LayoutSidebarDirective implements OnInit {
constructor(
router: Router,
private _sideBarSvc: SideBarService,
private sideBar: TemplateRef<any>,
private readonly categoryStore: BlogCategoryStore,
@Inject(BLOG_CATEGORY_TYPE) private categoryType: BlogCategoryType) {
router.events
.pipe(
filter(ev => ev instanceof NavigationEnd),
debounceTime(100),
takeUntilDestroyed()
)
.subscribe((ev) => {
this.updateSidebar();
});
}
ngOnInit() {
this.updateSidebar();
}
updateSidebar() {
if (this.sideBar) {
this.categoryStore.refresh(this.categoryType);
this._sideBarSvc.$sideBar.next(this.sideBar);
}
}
}