使用ANGLE的Title服务,更新HTMLTitleElement很容易。SPA中的每条路由都有不同的标题,这是很常见的。这通常是在路径组件的ngOnInit生命周期中手动完成的。然而,在这篇文章中,我们将以声明的方式使用@ngrx/路由器存储的功能和自定义的RouterStateSerializer和@ngrx/Effects来完成这项工作。
其概念如下:
- 在路线定义的数据中有一个标题属性。
- 使用@ngrx/store跟踪应用程序状态。
- 使用带有自定义RouterStateSerializer的@ngrx/router-store将所需标题添加到应用程序状态。
- 每次路径更改时,使用@ngrx/Effects来更新HTMLTitleElement,从而创建一个updateTitle效果。
项目设置
为了快速简单的设置,我们将使用@angular/angular。
1# Install @angular-cli if you don't already have it
2npm install @angular/cli -g
3
4# Create the example with routing
5ng new title-updater --routing
定义一些路径
创建两个组件:
1ng generate component gators
2ng generate component crocs
并定义他们的路线:
1[label title-updater/src/app/app-routing.module.ts]
2import { NgModule } from '@angular/core';
3import { Routes, RouterModule } from '@angular/router';
4import { GatorsComponent } from './gators/gators.component';
5import { CrocsComponent } from './crocs/crocs.component';
6
7const routes: Routes = [
8 {
9 path: 'gators',
10 component: GatorsComponent,
11 data: { title: 'Alligators'}
12 },
13 {
14 path: 'crocs',
15 component: CrocsComponent,
16 data: { title: 'Crocodiles'}
17 }
18];
请注意每个路由定义中的标题属性,它将用于更新HTMLTitleElement。
添加状态管理
@ngrx是管理应用程序状态的一个很棒的库。对于这个示例应用程序,我们将使用@ngrx/router-store将路由器序列化到@ngrx/store中,这样我们就可以监听路由更改并相应地更新标题。
<$>[注意]我们将使用@ngrx>4.0来利用新的RouterStateSerializer<$>
-对不起,先生.
1npm install @ngrx/store @ngrx/router-store --save
创建自定义RouterStateSerializer以将所需标题添加到州:
1[label title-updater/src/app/shared/utils.ts]
2import { RouterStateSerializer } from '@ngrx/router-store';
3import { RouterStateSnapshot } from '@angular/router';
4
5export interface RouterStateTitle {
6 title: string;
7}
8export class CustomRouterStateSerializer
9 implements RouterStateSerializer<RouterStateTitle> {
10 serialize(routerState: RouterStateSnapshot): RouterStateTitle {
11 let childRoute = routerState.root;
12 while (childRoute.firstChild) {
13 childRoute = childRoute.firstChild;
14 }
15// Use the most specific title
16const title = childRoute.data['title'];
17return { title };
定义路由器减少器:
1[label title-updater/src/app/reducers/index.ts]
2import * as fromRouter from '@ngrx/router-store';
3import { RouterStateTitle } from '../shared/utils';
4import { createFeatureSelector } from '@ngrx/store';
5
6export interface State {
7 router: fromRouter.RouterReducerState<RouterStateTitle>;
8}
9export const reducers = {
10 router: fromRouter.routerReducer
11};
每次@ngrx/store调度操作(路由器导航操作由StoreRouterConnectingModule发送)时,Reducer需要处理该操作并相应地更新状态。上面,我们将应用程序状态定义为具有路由器属性,并使用CustomRouterStateSerializer保持序列化的路由器状态。
还需要最后一步才能把这一切联系起来:
1[label title-updater/src/app/app.module.ts]
2import { NgModule } from '@angular/core';
3import { BrowserModule } from '@angular/platform-browser';
4import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store';
5import { StoreModule } from '@ngrx/store';
6
7import { AppRoutingModule } from './app-routing.module';
8import { AppComponent } from './app.component';
9import { CrocsComponent } from './crocs/crocs.component';
10import { GatorsComponent } from './gators/gators.component';
11import { reducers } from './reducers/index';
12import { CustomRouterStateSerializer } from './shared/utils';
13@NgModule({
14 declarations: [
15 AppComponent,
16 CrocsComponent,
17 GatorsComponent
18 ],
19 imports: [
20 BrowserModule,
21 AppRoutingModule,
22 StoreModule.forRoot(reducers),
23StoreRouterConnectingModule
24 ],
25 providers: [
26 /**
洒入魔法@ngrx/effect
现在,当我们切换路线时,我们的@ngrx/store将拥有我们想要的标题。要更新标题,我们现在所要做的就是监听ROUTER_NAVICATION操作并在状态上使用标题。我们可以使用@ngrx/Effects来实现这一点。
-对不起,先生.
1npm install @ngrx/effects --save
创建效果:
1[label title-updater/src/app/effects/title-updater.ts]
2import { Title } from '@angular/platform-browser';
3import { Actions, Effect } from '@ngrx/effects';
4import { ROUTER_NAVIGATION, RouterNavigationAction } from '@ngrx/router-store';
5import 'rxjs/add/operator/do';
6import { RouterStateTitle } from '../shared/utils';
7
8@Injectable()
9export class TitleUpdaterEffects {
10 @Effect({ dispatch: false })
11 updateTitle$ = this.actions
12 .ofType(ROUTER_NAVIGATION)
13 .do((action: RouterNavigationAction<RouterStateTitle>) => {
14 this.titleService.setTitle(action.payload.routerState.title);
15 });
最后,通过导入updateTitle效果来连接updateTitle效果,这将在通过订阅所有@Effect()来创建模块时开始监听效果:
1[label title-updater/src/app/app.module.ts]
2import { NgModule } from '@angular/core';
3import { BrowserModule } from '@angular/platform-browser';
4import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store';
5import { StoreModule } from '@ngrx/store';
6
7import { AppRoutingModule } from './app-routing.module';
8import { AppComponent } from './app.component';
9import { CrocsComponent } from './crocs/crocs.component';
10import { GatorsComponent } from './gators/gators.component';
11import { reducers } from './reducers/index';
12import { CustomRouterStateSerializer } from './shared/utils';
13import { EffectsModule } from '@ngrx/effects';
14import { TitleUpdaterEffects } from './effects/title-updater';
就是这样!您现在可以在路线定义中定义标题,它们将在路线更改时自动更新!
更进一步,从静态到动态⚡️
静态标题对于大多数用例来说都很好,但是如果你想通过名称欢迎用户或者显示通知计数呢?我们可以将路由数据中的title属性修改为接受上下文的函数。
下面是一个潜在的示例,如果商店上有通知计数:
1[label title-updater/src/app/app-routing.module.ts]
2import { NgModule } from '@angular/core';
3import { Routes, RouterModule } from '@angular/router';
4import { GatorsComponent } from './gators/gators.component';
5import { CrocsComponent } from './crocs/crocs.component';
6import { InboxComponent } from './inbox/inbox.component';
7
8const routes: Routes = [
9 {
10 path: 'gators',
11 component: GatorsComponent,
12 data: { title: () => 'Alligators' }
13 },
14 {
15 path: 'crocs',
16 component: CrocsComponent,
17 data: { title: () => 'Crocodiles' }
18 },
19 {
20 path: 'inbox',
21 component: InboxComponent,
22 data: {
23 // A dynamic title that shows the current notification count!
24 title: (ctx) => {
25 let t = 'Inbox';
26 if(ctx.notificationCount > 0) {
27 t += (${ctx.notificationCount});
28 }
29 return t;
30 }
31 }
32}
33];
1[label title-updater/src/app/effects/title-updater.ts]
2import { Title } from '@angular/platform-browser';
3import { Actions, Effect } from '@ngrx/effects';
4import { ROUTER_NAVIGATION, RouterNavigationAction } from '@ngrx/router-store';
5import { Store } from '@ngrx/store';
6import 'rxjs/add/operator/combineLatest';
7import { getNotificationCount } from '../selectors.ts';
8import { RouterStateTitle } from '../shared/utils';
9
10@Injectable()
11export class TitleUpdaterEffects {
12 // Update title every time route or context changes, pulling the notificationCount from the store.
13 @Effect({ dispatch: false })
14 updateTitle$ = this.actions
15 .ofType(ROUTER_NAVIGATION)
16 .combineLatest(this.store.select(getNotificationCount),
17 (action: RouterNavigationAction<RouterStateTitle>, notificationCount: number) => {
18 // The context we will make available for the title functions to use as they please.
19 const ctx = { notificationCount };
20 this.titleService.setTitle(action.payload.routerState.title(ctx));
21 });
现在,当加载收件箱路线时,用户还可以看到他们的通知计数也是实时更新的!💌
🚀继续试验和探索定制的RouterStateSerializers和@ngrx!