きなこもち.net

.NET Framework × UiPath,Orchestrator × Azure × AWS × Angularなどの忘備録

Angular × ルーティング × いろいろな画面遷移を実装してみた。

この記事の目的

この記事は、
Angularでいろいろな画面遷移を実現する方法をまとめること
を目的としています。

本題

成果物

成果物は以下の通りです!
stackblitz.com

ルーティング設定の追加

今回も、The online code editor for web apps. Powered by Visual Studio Code. - StackBlitzにあるAngular Materialのサンプルコードをベースに実装しています。Angular Materialのサンプルプロジェクトには、ルーティングの設定が入っていないので、手動で追加する必要があります。せっかく追加したので、忘備録として追加した方法をまとめておきます。

Step1) Angular Material のコンポーネントサンプルコードをベースにプロジェクトを作成
Step2) 1つ以上のComponentを作成する。

ここでは、ルートページとして最初に表示されるViewとComponentを作成しています。

  • home-page.component.ts
import {Component} from '@angular/core';

/**
 * @title Button varieties
 */
@Component({
  selector: 'home-page',
  templateUrl: 'home-page.component.html',
  styleUrls: ['home-page.component.css'],
})
export class HomePage {}
  • home-page.component.css(空ファイル)
  • home-page.component.html
<h1>Home Page</h1>
Step2) 以下の3ファイルを作成する
  • app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'Sample';
}
  • app.component.css (空ファイル)
  • app.component.html
<h1>{{title}}</h1>
<nav>
  <a routerLink="/home">Home</a>
</nav>
<router-outlet></router-outlet>
Step3)app-routing.module.tsを作成する。
  • app-routing.module.ts
import { NgModule }             from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomePage } from './home-page.component';

const routes: Routes = [
  { path: '', redirectTo: '/home', pathMatch: 'full' },
  {path:'home',  component: HomePage}
];

@NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  exports: [ RouterModule ]
})
export class AppRoutingModule {}
Step4) main.tsファイルに、新規ファイルたちの設定を追加する。
declarations: [
            HomePage,
            AppComponent
           ],
bootstrap: [ 
            AppComponent 
           ]
Step5) Index.htmの編集
<app-root></app-root>


ここまでで、新しく作成したHomeページへのルーティング設定が有効になります。ページを追加したい場合は、ページ作成後、app-routing.moduleに新しいページの情報を追加していく感じになります。

Router Linkを使った画面遷移の例

app-routing,module.tsで設定したPathとComponentの組み合わせに従ってルーティングされるます。View側(HTML)で遷移処理を設定したい場合は、routerLink属性にパスを設定します。Component側(typescript)で遷移処理を設定したい場合は、Router Compoentのnavigateメソッドを利用します。

遷移だけの場合(サンプルコード:Page1)

RouterにPathをComponentを設定するだけ。

遷移元から、遷移先へパラメータを渡す場合(サンプルコード:Page3)
  • Routerに設定するPathに、パラメータの設定を追加する必要がある。
  • 受け取り側は、ActivatedRoute Compornetを使って、受け取ったパラメータリストの中から、対象のパラメータ名を使って値を取り出す。
//id という名前のキーをRouterで設定した場合のサンプル
 this.activatedRoute.params.subscribe(params => (this.id = params["id"]));



特定画面からの遷移しか受け付けないようにする

Webアプリケーションでは、たびたびURL直打ち遷移や、不正画面遷移の検出といった要件があります。そんな仕様に対しては、セッションを使って実現することが多いかと思います。しかし、Angularでは、Webサーバー側(バックエンド側)でのセッション管理ができません。こういった場合、どのように実現するかもまとめてみました。

サンプルアプリケーションでは、Page4のセクションでまとめて実装しています。

Page4には、Page4を含めて4つの画面があります。Page4-1 ~ Page4-3と、Page4です。仕様は、遷移元がPage4-2の時だけ、Page4-3に遷移できるようになっています。そのほかの画面からの遷移を検出した場合、Homeページへ強制遷移させるようになっています。

遷移元画面のURLの取得に関しては、PreviousRouteService.tsでサービスとして定義しています。

import { Injectable } from "@angular/core";
import { Router, RoutesRecognized } from "@angular/router";
import { filter, pairwise } from "rxjs/operators";

@Injectable({
  providedIn: "root"
})
export class PreviousRouteService {
  private previousUrl: string;
  private currentUrl: string;

  constructor(private router: Router) {
    this.currentUrl = this.router.url;
    this.previousUrl = "";
    console.info("Executed");
    this.router.events
      .pipe(
        filter((event: any) => event instanceof RoutesRecognized),
        pairwise()
      )
      .subscribe((events: RoutesRecognized[]) => {
        this.previousUrl = events[0].urlAfterRedirects;
        this.currentUrl = events[1].urlAfterRedirects;
      });
  }

  public getPreviousUrl() {
    return this.previousUrl;
  }
 //現在のページ情報の取得を追加しました。
  public getCurrentUrl() { 
    return this.currentUrl;
  }
}


参考)
ionic4 - How to dynamically go to previous page (ionic 4)? - Stack Overflow


遷移元画面では、OnInit処理の中で、上記のServiceから取得した遷移元画面のURLを使い、Page4-2以外のページからの遷移を検出しています。

  ngOnInit() {
    if (this.previousUrl !=="/page4-2"){
      console.info("previous URL is not Page4-2.")
      this.router.navigate(["/home"]);
    }
  }


追記)Routerには、ガードという機能があるらしい・・・
Canで始まるインターフェースを実装することで、特定のタイミングでルーティングに関するチェック処理を組み込むことができるみたいです。On Initで拒否するのではなく、このインターフェースを実装する形で拒否するようにするのがよりよさそう。
angular.jp
f:id:kinakomotitti:20200726002410p:plain

ng-ifを使った画面遷移の例

”画面遷移”という扱いになるのかわからないですが、こういう画面遷移もやってみました。
サンプルは、成果物の中のPage1です。

基本方針としては、上記のようなRouterを介さずに画面遷移風な挙動を実現するというものになっています。まず、親のページと、複数の子ページを用意します。そして、親ページの中に、子ページのタグを追加します。あとは、Ng-Ifをうまく利用して、子ページの表示・非表示を切り替えていく形になります。
子ページへの値わたしや、受け取りは、@input/outputを使うことで可能ですし、親ー子間での双方向のバインドも可能です。※サンプルでは、PageMode変数を双方向バインドしています。

Sampleでは、PageModeというプロパティをComponent側に持たせ、それをフラグ的に利用しています。

<div *ngIf="pageMode==='0'" >
<h2>
 Page1 : ngIfを使って遷移を表現しています。
</h2>

<mat-radio-group [(ngModel)]="pageMode" 
                 aria-label="Select an option">
  <mat-radio-button value="1">Page 1</mat-radio-button>
  <mat-radio-button value="2">Page 2</mat-radio-button>
</mat-radio-group>
</div>

<page1-1 *ngIf="pageMode==='1'" [(pageMode)]="pageMode"></page1-1>

<page1-2 *ngIf="pageMode==='2'" [(pageMode)]="pageMode"></page1-2>


Modal機能を使った画面遷移の例

そのほかの画面遷移方式として、思いつくのが、Modalページを使った遷移です。
サンプルは、Page2となります。まぁ、ほぼMaterianのサンプルを利用しているだけですが・・・

まとめ

今回は、ぱっと思いつく画面遷移の実装方法に焦点を当ててまとめてみました。他にも実装方式がみつかったら、追加していこうと思います。
ただ、今回はあくまで画面遷移のところだけのお話でしたので、Routerを介した画面遷移の場合のRouting設計については、触れられていません。単純に画面を増やして、ルーティング設定を増やしていくと、後々保守が難しくなります。たぶん。
なので、保守を意識したルーティング設計という観点で、引き続き調べていこうと思います。