Angular 2.0.1:简化复杂的导航组件
Angular 2.0.1: simplifying a complex navigation component
如何使导航组件与重要应用程序保持同步?例如,在 Angular 2's simple Tour of Heroes application 中,app.component.ts
具有静态(即不变的)声明式导航:
<h1>{{title}}</h1>
<nav>
<a routerLink="/dashboard" routerLinkActive="active">Dashboard</a>
<a routerLink="/heroes" routerLinkActive="active">Heroes</a>
</nav>
<router-outlet></router-outlet>
但在更复杂的应用程序中,导航组件通常是动态的。在我的例子中,nav
有一个 sidenav,一个 back 按钮,特定于组件的 actions,以及用户信息;并且,sidenav 和 actions 会根据用户的角色而变化。这是一个相当典型的场景——想想 Google Gmail、Inbox、Calendar、Docs 等
现在,我的 nav
在一个单独的 my-header
组件中,该组件实现了针对当前组件和用户角色的大量 *ngIf
测试——这是一场丑陋的噩梦:
<md-toolbar color="primary" class="fixed-toolbar">
<div *ngIf="isAuthenticated()">
<md-menu #mainmenu="mdMenu">
<a md-menu-item routerLink="/marketplaces" routerLinkActive="active">Marketplaces</a>
<a md-menu-item routerLink="/people" routerLinkActive="active">People</a>
<span *ngIf="isAdministrator()">
<md-divider></md-divider>
<a md-menu-item routerLink="/accounts" routerLinkActive="active">Accounts</a>
</span>
</md-menu>
<button *ngIf="_navigation?.toolbar == 'default'" md-icon-button [md-menu-trigger-for]="mainmenu">
<md-icon>menu</md-icon>
</button>
<button *ngIf="_navigation?.toolbar == 'market'" md-icon-button
(click)="closeAndReturn($event, 'marketplaces')"
aria-label="Back to Marketplaces">
<md-icon>close</md-icon>
</button>
<button *ngIf="_navigation?.toolbar == 'person'" md-icon-button
(click)="closeAndReturn($event, 'users')"
aria-label="Back to People">
<md-icon>close</md-icon>
</button>
</div>
<!-- ^^^ nightmare continues vvv -->
my-header
组件简单地包含在 app.component.html
:
<my-header></my-header>
<router-outlet></router-outlet>
<my-footer></my-footer>
但是 my-header
组件和 my-footer
组件都不在路由器树中,所以 my-header
注入的 ActivatedRoute
始终是根节点,这意味着我可以'甚至转换绝对 返回 导航:
(click)="closeAndReturn($event, 'marketplaces')"
相对导航:
(click)="closeAndReturn($event, '../')"
这意味着在这个组件中复制路由器树以保持同步——这显然不是正确的做法...
关于如何解决让导航组件与复杂应用程序保持同步这一可能非常普遍的挑战,有什么想法吗?
为了解决相对导航方面的问题,监听路由器中的事件并捕获 URL:
constructor(private _router: Router) {
this._router.events
.filter( e => e instanceof NavigationEnd )
.subscribe( e => this._currentUrl = e.url );
}
然后,trim最后一个URL段要更上一层楼:
closeAndReturn(event?) {
const url = this._currentUrl;
const path = url.slice(0, url.lastIndexOf('/'));
this._router.navigate([path]);
}
HTML 更简洁,重复更少:
<button md-icon-button (click)="closeAndReturn($event)" aria-label="Back">
<md-icon>close</md-icon>
</button>
但是HTML中仍然有很多重复的路由器信息。
如何使导航组件与重要应用程序保持同步?例如,在 Angular 2's simple Tour of Heroes application 中,app.component.ts
具有静态(即不变的)声明式导航:
<h1>{{title}}</h1>
<nav>
<a routerLink="/dashboard" routerLinkActive="active">Dashboard</a>
<a routerLink="/heroes" routerLinkActive="active">Heroes</a>
</nav>
<router-outlet></router-outlet>
但在更复杂的应用程序中,导航组件通常是动态的。在我的例子中,nav
有一个 sidenav,一个 back 按钮,特定于组件的 actions,以及用户信息;并且,sidenav 和 actions 会根据用户的角色而变化。这是一个相当典型的场景——想想 Google Gmail、Inbox、Calendar、Docs 等
现在,我的 nav
在一个单独的 my-header
组件中,该组件实现了针对当前组件和用户角色的大量 *ngIf
测试——这是一场丑陋的噩梦:
<md-toolbar color="primary" class="fixed-toolbar">
<div *ngIf="isAuthenticated()">
<md-menu #mainmenu="mdMenu">
<a md-menu-item routerLink="/marketplaces" routerLinkActive="active">Marketplaces</a>
<a md-menu-item routerLink="/people" routerLinkActive="active">People</a>
<span *ngIf="isAdministrator()">
<md-divider></md-divider>
<a md-menu-item routerLink="/accounts" routerLinkActive="active">Accounts</a>
</span>
</md-menu>
<button *ngIf="_navigation?.toolbar == 'default'" md-icon-button [md-menu-trigger-for]="mainmenu">
<md-icon>menu</md-icon>
</button>
<button *ngIf="_navigation?.toolbar == 'market'" md-icon-button
(click)="closeAndReturn($event, 'marketplaces')"
aria-label="Back to Marketplaces">
<md-icon>close</md-icon>
</button>
<button *ngIf="_navigation?.toolbar == 'person'" md-icon-button
(click)="closeAndReturn($event, 'users')"
aria-label="Back to People">
<md-icon>close</md-icon>
</button>
</div>
<!-- ^^^ nightmare continues vvv -->
my-header
组件简单地包含在 app.component.html
:
<my-header></my-header>
<router-outlet></router-outlet>
<my-footer></my-footer>
但是 my-header
组件和 my-footer
组件都不在路由器树中,所以 my-header
注入的 ActivatedRoute
始终是根节点,这意味着我可以'甚至转换绝对 返回 导航:
(click)="closeAndReturn($event, 'marketplaces')"
相对导航:
(click)="closeAndReturn($event, '../')"
这意味着在这个组件中复制路由器树以保持同步——这显然不是正确的做法...
关于如何解决让导航组件与复杂应用程序保持同步这一可能非常普遍的挑战,有什么想法吗?
为了解决相对导航方面的问题,监听路由器中的事件并捕获 URL:
constructor(private _router: Router) {
this._router.events
.filter( e => e instanceof NavigationEnd )
.subscribe( e => this._currentUrl = e.url );
}
然后,trim最后一个URL段要更上一层楼:
closeAndReturn(event?) {
const url = this._currentUrl;
const path = url.slice(0, url.lastIndexOf('/'));
this._router.navigate([path]);
}
HTML 更简洁,重复更少:
<button md-icon-button (click)="closeAndReturn($event)" aria-label="Back">
<md-icon>close</md-icon>
</button>
但是HTML中仍然有很多重复的路由器信息。