0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Angularと戯れる⑥

Posted at

はじめに

Angularチュートリアル:Tour of Heroesを読み進めていきます。

開発環境

  • OS: Windows10
  • Nodeバージョン: 18.18.0
  • Angular CLI バージョン: 17.3.8
  • エディタ: VSCode

5. ナビゲーションの追加

5. ナビゲーションの追加を読んでいきます。

アプリ構成

nav-diagram.png

https://v17.angular.jp/tutorial/tour-of-heroes/toh-pt5 に添付されている図より。

  1. アプリのトップ画面にダッシュボード用のボタンとヒーロー一覧画面(こらまで作ってきたHeroesComponent)を表示する用のボタンを配置する
  2. ダッシュボードからヒーロー詳細画面に遷移できる
  3. ヒーロー一覧画面からヒーロー詳細画面に遷移できる
  4. ヒーロー詳細画面には「戻る」ボタンを配置し、押下することで、遷移元の画面に戻ることができる

関連コマンド

ng generate module app-routing --flat --module=app

これにより以下のことが起こります。

  • src/app フォルダに app-routing.module.ts が生成される
    • --flatオプションをつけることで、固有のディr区鳥の代わりに、src/appに生成したファイルが配置される。
生成された src/app/app-routing.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

@NgModule({
  imports: [
    CommonModule
  ],
  declarations: []
})
export class AppRoutingModule { }
  • src/app/app.module.tsが、このapp-routing.module.ts をインポートする
    • --module=appオプションを付けることで、AppModuleのimports配列に、生成したモジュールを登録するようCLIに指示する。
src/app/app.module.ts
...
import { AppRoutingModule } from './app-routing.module';

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...,
    AppRoutingModule
  ],
  ...
})

ルーティング定義を書くのであれば、app.module.tsに書くでもいけそうですが(構造同じですし)、ルーティングに関する記載はapp.module.tsの外に出して、分割できるのであれば分割するのがAngular流なのかなという理解をしました。

今後ルーティング定義はapp-routing.module.ts に書いていくことになります。

ソースコードを読み解いてみる

Routeの設定: RouterModule.forRoot()

例えば以下のように記載すると、「localhost:4200/heroes」というURLが入力されたとき、HeroesCoponentの内容が記載されるという意味になります。

src/app/app-routing.module.ts
import { RouterModule, Routes } from '@angular/router';
...

const routes: Routes = [
  { path: 'heroes', component: HeroesComponent }
];

「URL末尾に何もなければこのURLのパターンにリダイレクトする」「ID等の数字をURLのパスのパターンとして登録する」こともできます。

src/app/app-routing.module.ts
import { RouterModule, Routes } from '@angular/router';
...

const routes: Routes = [
  // URLの末尾に「/heroes」をつけると、HeroesComponentの内容が表示される
  {path: "heroes", component: HeroesComponent},
  // URLの末尾に「/dashboard」をつけると、DashboardComponentの内容が表示される
  {path: "dashboard", component: DashboardComponent},
  // URLの末尾に何も入力されていない(空という文字列に完全一致している)場合、dashboardにリダイレクトする
  {path: "", redirectTo: "/dashboard", pathMatch: "full"},
  // URLの末尾に「/detail/%id%」をつけることで該当のHeroDetailComponentの内容が表示される
  { path: "detail/:id", component: HeroDetailComponent }
]

その設定を適用させます。
これにより、「このパスのパターンの場合はこれを表示する」という対応をアプリに登録することになります。

src/app/app-routing.module.ts
import { RouterModule, Routes } from '@angular/router';
...

imports: [ RouterModule.forRoot(routes) ],

そしてそのRouterModuleを共有できるようにexportします。

src/app/app-routing.module.ts
import { RouterModule, Routes } from '@angular/router';
...

exports: [ RouterModule ]

RouterOutlet

コンポーネント内で<router-outlet>タグを使います。

src/app/app.component.html
<h1>{{title}}</h1>
<router-outlet></router-outlet>
<app-messages></app-messages>

<router-outlet>を使うことで、現在ルーティングされているコンテンツを 動的に切り替えて表示 できます。

つまり、

  • ~/heroes というURLが指定されたら、<router-outlet>の部分にはそれに対応するHeroesComponentの内容が表示されるし、
  • ~/dashboard というURLが指定されたら、<router-outlet>の部分にはそれに対応するDashboardComponsntの内容が表示される

ということです。
<h1><app-messages>はルーティングとして何が指定されていようが固定で表示される)

RouterLink

src/app/app.component.html
...
<nav>
  <a routerLink="/dashboard">Dashboard</a>
  <a routerLink="/heroes">Heroes</a>
</nav>

<router-outlet></router-outlet>
<app-messages></app-messages>

routerLink属性を使ってパスを指定することで、そのパスのパターンにマッチするコンポーネントにリンクを飛ばすことができます。

たとえば、Heroesという文字列が表示されたリンクを踏むと、

  1. routerLink="/heroes"のパターンをRouterに問い合わせる
  2. app-routing.module.ts を見ると、そのパターンにマッチするコンポーネントはHeroesComponentだと分かる
  3. HeroesComponentの内容を表示すべきと判断する(表示場所は<router-outlet>
src/app/app-routing.module.ts
...

const routes: Routes = [
  // このパターンにマッチ
  {path: "heroes", component: HeroesComponent},
  ...
]

という流れで画面が表示されます。

なお、リンクを飛ばすといえば<a href="〇〇>"ですが、この手法だとページ全体に更新がかかります。

routerLinkを使うことで、ページ全体ではなく該当する箇所のみに更新がかかるようになります。
SPA(Single Page Application)の特性と関連したテクニックといえるでしょう。

RouterLink(その2)

URLにID等の数字を入れてページを表示する場合もあります。
 >例: localhost:4200/detail/13

その数字は固定ではなく、13のときもあれば15のときもあります。
こうした動的な値を含んだURLへのリンクを飛ばすテクニックについて書いていきます。

ルーティングのパターンの登録

src/app/app-routing.module.ts
...
const routes: Routes = [
  ...
  // URLの末尾に「/detail/%id%」をつけることで該当のHeroDetailComponentの内容が表示される
  { path: "detail/:id", component: HeroDetailComponent }
]

パスの指定をよく見ると、:idと書いています。
コロンを付けることで、 「idは固定の文字列ではなく変数です」という意味を持たせる ことができます。

リンクを飛ばす(RouterLinkの利用)

src/app/dashboard.component.html
<a *ngFor="let hero of heroes"
  routerLink="/detail/{{hero.id}}">
  {{hero.name}}
</a>

Heroオブジェクトのidを取得し、それをダブルカーリーグレイシスを使うことで、変数idに値を動的に埋め込んでいます。

これにより、「動的な値を含むURLへのリンクを飛ばす」を実現します。

補足

ここまでは「idを渡す側」の視点でのテクニックでしたが、「idを受け取った側」のテクニックも記載します。

具体的には、

  • idを受け取り、
  • HEROES(Heroオブジェクトの配列のモックデータ)から該当のidにマッチするHeroオブジェクトを取得して返す

ということを行います。

@angular/routerActivatedRouteを使います。

src/app/hero-detail/hero-detail.component.ts
import { ActivatedRoute } from '@angular/router';
...
export class HeroDetailComponent {
  constructor(
    private route: ActivatedRoute,
    private heroService: HeroService,
    private location: Location
  ) {}
  
  ngOnInit(): void {
    this.getHero();
  }
  
  getHero(): void {
    const id = 
  Number(this.route.snapshot.paramMap.get('id'));
    this.heroService.getHero(id)
      .subscribe(hero => this.hero = hero);
  }
}

Number(this.route.snapshot.paramMap.get('id'))では、URLのパラメタからidの値を取り出し(~/detail/13 というURLが指定されたのなら"13" という値が取得される)、それをnumberに変換しているという処理をしています。

(AIにも聞いてみた)

このコードは、Angular のルーティング機能を使用して、URL パラメータから id を取得し、それを数値に変換する処理を行っています。以下にステップバイステップで説明します:

1.this.route.snapshot:

  • ActivatedRoute の snapshot プロパティを使用して、現在のルートの静的なスナップショットを取得します。snapshot は、ルートが変わるたびに更新されるオブジェクトです。

2.paramMap:

  • snapshot.paramMap は、現在のルートに関連するすべてのパラメータを含む ParamMap オブジェクトを返します。ParamMap は、ルートパラメータをキーと値のペアとして管理します。

3.get('id'):

  • paramMap.get('id') は、ParamMap から id パラメータの値を取得します。この値は文字列として返されます。

4.Number(...):

  • Number(this.route.snapshot.paramMap.get('id')) は、取得した id パラメータの値を数値に変換します。これにより、id が数値として扱われるようになります。

まとめ
このコードは、URL パラメータから id を取得し、それを数値に変換して id 変数に格納する処理を行っています。これにより、例えば /heroes/1 のような URL から 1 という数値を取得することができます。

遷移元の画面に戻る

@angular/commonLocationを使います。

ボタン配置

src/app/hero-detail/hero-detail.component.html
...
<button type="button" (click)="goBack()">Go Back</button>

「Go Back」と表示されているボタンを押下すると、goBack() メソッドが実行されます。
イベントバインディングの仕組みですね。

戻る処理の実装

src/app/hero-detail/hero-detail.component.ts
import { Location } from '@angular/common';
...

export class HeroDetailComponent {
  ...

  constructor(
    ...,
    private location: Location
  ) {}

  ...

  goBack(): void {
    this.location.back();
  }
}

@angular/commonLocationが提供しているback() メソッドを実行することで、遷移元の画面に戻ることができます。

つまり、

  • 「ダッシュボードからヒーロー詳細画面に遷移したのであれば、戻るボタンを押下すると、ダッシュボード画面が表示される」し、
  • 「ヒーロー一覧画面からヒーロー詳細画面に遷移したのであれば、戻るボタンを押下すると、ヒーロー一覧画面が表示される」

ということです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?