経緯
Webサイトを作る際、現在表示しているページのタイトルを、ブラウザのタイトルバーに表示するのがアクセシビリティの面から望まれます。 Single Page Application(以下SPAと表記)では、URI的には複数のページが、実体としては1つにということになります。SPAでは表示しているURIに応じてタイトルバーの表示を変更する必要が出てきます。
SPAサイトをAngularで実装していたのですが、単にページへのリンクを押して遷移するわけではなく、スクリプトが特定の条件を満たした場合に遷移するので、Routerを使ってページ遷移を実現することになります。このパターンの実装方法はネットで探せばまあまあ出てくるのですが、実装してみると動かないことが多々ありました。特定のAngularバージョンでしか動作しないか、記載の間違いと思われます。思いのほか難航してしまったので、解決できた実装内容などを残しておきます。
実装方法 : Angular 13まで
こちらの記事「Setting Page Titles Natively With The Angular Router」で紹介されていました。著者はGDE(Google Developers Experts)の方なので確実な実装だと思います。この記事は前半が現在の最新版であるAngular 13までの実装方法です。
StackOverflowの記事「How to change page title with routing in Angular application?」の、Ankurさんの回答も多少の違いはありますが、ほぼ同じです。
まずは各ページのデータを用意します。ここで任意のデータを渡せる dataプロパティを使ってページタイトルを含めます。
@NgModule({
    imports: [
        RouterModule.forRoot([
            { path: 'page-path', component: PageComponent, data:{ title: 'タイトル' }, },
        ]),
        ...
    ],
    ...
})
ページ表示時にタイトルを反映させる処理がこちら。ループで最もチェインの先端のRouteを取得していますが、ページが入れ子になっているケースに対応するためです。
import { Title } from '@angular/platform-browser';
import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
import { filter, map } from 'rxjs';
@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    providers: [ Title ],
})
export class AppComponent {
    constructor(
     private router: Router,
     private pageTitle: Title,
    ) { }
    ngOnInit() {
        this.router.events
         .pipe(
            filter(event => event instanceof NavigationEnd),    // NavigationEndのタイミングで
            map(() => {
                let route: ActivatedRoute = this.router.routerState.root;
                while (route.firstChild) {
                    route = route.firstChild;                   // 最も下位のRouteを取得して
                }
                if (route.snapshot.data['title']) {
                    return route.snapshot.data['title'];        // そのRouteのdataからタイトルを取得
                }
                return null;
            })
         )
         .subscribe((title: string) => {
            if (title) {
                this.title.setTitle(title);                 // 取得したタイトルを反映
            }
         });
    }
}
実装方法 : Angular 14から
上記の記事の後半「Using the built-in TitleStrategy」からは、Angular 14での実装について書かれています(Angular 14はまだプレリリース版のようです)。TitleStrategyクラスを使用することで、より簡単に実装できるようです。
以下のスニペットは記事をもとに書いてみただけです。まだAngular 14の開発環境を用意していないので、動作確認していません。こんな感じになるくらいに認識してください。
各ページのタイトルのために、Routeクラスに専用のプロパティtitleが追加されています。もうdataプロパティは使わなくても構いません。
@NgModule({
    imports: [
        RouterModule.forRoot([
            { path: 'page-path', component: PageComponent, title: 'タイトル' },
        ]),
        ...
    ],
    ...
})
ページ表示時にタイトルを反映させる処理は、AppComponentではなくTitleStrategyクラスから派生したサブクラスでupdateTitle()メソッドをオーバーライドして実装します。下記のbuildTitle()メソッドはTitleStrategyクラスのものなのか、サブクラスに自分で実装するのかは、説明がないので不明です。
@Injectable()
export class TemplatePageTitleStrategy extends TitleStrategy {
    constructor(private readonly title: Title) {
        super();
    }
    override updateTitle(routerState: RouterStateSnapshot) {
        const title = this.buildTitle(routerState);
        if (title) {
            this.title.setTitle(title);
        }
    }
}
ソースファイルは増えますが、この実装の方がより直感的で魅力的になったように感じます。
公式痛恨(?)のフライング...
Angularの日本語サイトを彷徨っていたら「Setting the page title」を見つけました。参照したのはAngular 13のstable(v13.3.3)なのですが、この章の記載内容は上記のAngular 14そのものです。フライングで公開してしまったようです。
0 件のコメント:
コメントを投稿