HTMLTitleElement の更新は、AngularのTitleサービスで簡単に行えます。 SPAの各ルートが異なるタイトルを持つことはかなり一般的です。 これは、ルートのコンポーネントのngOnInitライフサイクルで手動で行われることがよくあります。 ただし、この投稿では、 @ ngrx /router-storeのパワーとカスタムRouterStateSerializerおよび@ngrx / Effects を使用して、宣言的な方法でそれを行います。 ]。

コンセプトは次のとおりです。

  • ルート定義のdatatitleプロパティがあります。
  • @ 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の実験と探索を続けてください!