Angularとngrxを使用した宣言型タイトルアップデータ
HTMLTitleElement の更新は、AngularのTitleサービスで簡単に行えます。 SPAの各ルートが異なるタイトルを持つことはかなり一般的です。 これは、ルートのコンポーネントのngOnInitライフサイクルで手動で行われることがよくあります。 ただし、この投稿では、 @ ngrx /router-storeのパワーとカスタムRouterStateSerializerおよび@ngrx / Effects を使用して、宣言的な方法でそれを行います。 ]。
コンセプトは次のとおりです。
- ルート定義のdataにtitleプロパティがあります。
- @ ngrx / store を使用して、アプリケーションの状態を追跡します。
- @ ngrx /router-storeとカスタムRouterStateSerializerを使用して、目的のtitleをアプリケーションの状態に追加します。
- @ ngrx /Effectsを使用してupdateTitleエフェクトを作成し、ルートが変更されるたびにHTMLTitleElementを更新します。
プロジェクトの設定
すばやく簡単にセットアップするために、 @ angle /cliを使用します。
# Install @angular-cli if you don't already have it npm install @angular/cli -g # Create the example with routing ng new title-updater --routing
いくつかのルートの定義
いくつかのコンポーネントを作成します。
ng generate component gators ng generate component crocs
そして、それらのルートを定義します。
title-updater / src / app / app-routing.module.ts
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { GatorsComponent } from './gators/gators.component'; import { CrocsComponent } from './crocs/crocs.component'; const routes: Routes = [ { path: 'gators', component: GatorsComponent, data: { title: 'Alligators'} }, { path: 'crocs', component: CrocsComponent, data: { title: 'Crocodiles'} } ];
各ルート定義のtitleプロパティに注意してください。これは、HTMLTitleElementを更新するために使用されます。
状態管理を追加する
@ngrx は、アプリケーションの状態を管理するための優れたライブラリです。 このサンプルアプリケーションでは、 @ ngrx / router-store を使用して、ルーターを @ ngrx / store にシリアル化し、ルートの変更をリッスンし、それに応じてタイトルを更新できるようにします。
@ngrx > 4.0を使用して、新しいRouterStateSerializerを活用します。
インストール:
npm install @ngrx/store @ngrx/router-store --save
カスタムRouterStateSerializerを作成して、目的のタイトルを状態に追加します。
title-updater / src / app / shared / utils.ts
import { RouterStateSerializer } from '@ngrx/router-store'; import { RouterStateSnapshot } from '@angular/router'; export interface RouterStateTitle { title: string; } export class CustomRouterStateSerializer implements RouterStateSerializer<RouterStateTitle> { serialize(routerState: RouterStateSnapshot): RouterStateTitle { let childRoute = routerState.root; while (childRoute.firstChild) { childRoute = childRoute.firstChild; } // Use the most specific title const title = childRoute.data['title']; return { title };
ルーターレデューサーを定義します。
title-updater / src / app / reducers / index.ts
import * as fromRouter from '@ngrx/router-store'; import { RouterStateTitle } from '../shared/utils'; import { createFeatureSelector } from '@ngrx/store'; export interface State { router: fromRouter.RouterReducerState<RouterStateTitle>; } export const reducers = { router: fromRouter.routerReducer };
@ ngrx / store がアクションをディスパッチするたびに(ルーターナビゲーションアクションは StoreRouterConnectingModule によって送信されます)、レデューサーはそのアクションを処理し、それに応じて状態を更新する必要があります。 上記では、 CustomRouterStateSerializer を使用して、ルータープロパティを持ち、シリアル化されたルーターの状態を維持するようにアプリケーションの状態を定義しています。
すべてを接続するには、最後の1つのステップが必要です。
title-updater / src / app / app.module.ts
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store'; import { StoreModule } from '@ngrx/store'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { CrocsComponent } from './crocs/crocs.component'; import { GatorsComponent } from './gators/gators.component'; import { reducers } from './reducers/index'; import { CustomRouterStateSerializer } from './shared/utils'; @NgModule({ declarations: [ AppComponent, CrocsComponent, GatorsComponent ], imports: [ BrowserModule, AppRoutingModule, StoreModule.forRoot(reducers), StoreRouterConnectingModule ], providers: [ /**
魔法を振りかける@ngrx/効果
これで、ルートを切り替えると、 @ ngrx /storeに必要なタイトルが付けられます。 タイトルを更新するには、 ROUTER_NAVIGATION アクションをリッスンし、状態でタイトルを使用するだけです。 これは@ngrx /Effectsで実行できます。
インストール:
npm install @ngrx/effects --save
効果を作成します。
title-updater / src / app / Effects / title-updater.ts
import { Title } from '@angular/platform-browser'; import { Actions, Effect } from '@ngrx/effects'; import { ROUTER_NAVIGATION, RouterNavigationAction } from '@ngrx/router-store'; import 'rxjs/add/operator/do'; import { RouterStateTitle } from '../shared/utils'; @Injectable() export class TitleUpdaterEffects { @Effect({ dispatch: false }) updateTitle$ = this.actions .ofType(ROUTER_NAVIGATION) .do((action: RouterNavigationAction<RouterStateTitle>) => { this.titleService.setTitle(action.payload.routerState.title); });
最後に、 updateTitleエフェクトをEffectsModule.forRootでインポートしてフックアップします。これにより、すべての @Effect()にサブスクライブしてモジュールが作成されると、エフェクトのリッスンが開始されます。 :
title-updater / src / app / app.module.ts
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store'; import { StoreModule } from '@ngrx/store'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { CrocsComponent } from './crocs/crocs.component'; import { GatorsComponent } from './gators/gators.component'; import { reducers } from './reducers/index'; import { CustomRouterStateSerializer } from './shared/utils'; import { EffectsModule } from '@ngrx/effects'; import { TitleUpdaterEffects } from './effects/title-updater';
以上です! ルート定義でタイトルを定義できるようになり、ルートが変更されると自動的に更新されます。
さらに進んで、静的から動的へ⚡️
静的なタイトルはほとんどのユースケースに最適ですが、名前でユーザーを歓迎したり、通知数も表示したりしたい場合はどうでしょうか。 ルートデータのtitleプロパティを変更して、コンテキストを受け入れる関数にすることができます。
notifyCountがストアにあった場合の潜在的な例を次に示します。
title-updater / src / app / app-routing.module.ts
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { GatorsComponent } from './gators/gators.component'; import { CrocsComponent } from './crocs/crocs.component'; import { InboxComponent } from './inbox/inbox.component'; const routes: Routes = [ { path: 'gators', component: GatorsComponent, data: { title: () => 'Alligators' } }, { path: 'crocs', component: CrocsComponent, data: { title: () => 'Crocodiles' } }, { path: 'inbox', component: InboxComponent, data: { // A dynamic title that shows the current notification count! title: (ctx) => { let t = 'Inbox'; if(ctx.notificationCount > 0) { t += (${ctx.notificationCount}); } return t; } } } ];
title-updater / src / app / Effects / title-updater.ts
import { Title } from '@angular/platform-browser'; import { Actions, Effect } from '@ngrx/effects'; import { ROUTER_NAVIGATION, RouterNavigationAction } from '@ngrx/router-store'; import { Store } from '@ngrx/store'; import 'rxjs/add/operator/combineLatest'; import { getNotificationCount } from '../selectors.ts'; import { RouterStateTitle } from '../shared/utils'; @Injectable() export class TitleUpdaterEffects { // Update title every time route or context changes, pulling the notificationCount from the store. @Effect({ dispatch: false }) updateTitle$ = this.actions .ofType(ROUTER_NAVIGATION) .combineLatest(this.store.select(getNotificationCount), (action: RouterNavigationAction<RouterStateTitle>, notificationCount: number) => { // The context we will make available for the title functions to use as they please. const ctx = { notificationCount }; this.titleService.setTitle(action.payload.routerState.title(ctx)); });
これで、 Inbox ルートが読み込まれると、ユーザーはリアルタイムで更新される通知カウントも確認できます。 💌
🚀カスタムRouterStateSerializersと@ngrxの実験と探索を続けてください!