実践 Angular: Standalone Components をざっくりキャッチアップしてみた Part3
lacolaco さん監修の実践 Angular: Standalone Components をざっくりキャッチアップしてみます。 zenn.dev
- Google Developers Expert for Angular
- Angularコントリビューター
- Angular日本ユーザー会代表
- jsprimer.net 著者
Zenn プロフィールから引用
前回
■ memo
- withHashLocation: ハッシュロケーションを使う
- withDebugTracing: デバッグログを有効にする
- withInMemoryScrolling: ページ遷移時のスクロール位置を記憶する
ルーターのセットアップ準備
■ config, route module の作成
$ cd ~/stand-alone-components-sample/src/app # config module生成(configファイルのディレクトリ移動、ファイル名変更、移動元ディレクトリ削除) $ npx ng g m config && mv ./config/config.module.ts ./app.config.ts && rm -rf ./config # route module生成(routeファイルのディレクトリ移動、ファイル名変更、移動元ディレクトリ削除) $ npx ng g m route && mv ./route/route.module.ts ./app.route.ts && rm -rf ./route
- app.config.ts
// この段階では、ファイル作成と class 名変更のみ // この後の作業で書き換えます import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; @NgModule({ declarations: [], imports: [ CommonModule ] }) export class ConfigModule { }
- app.route.ts
// この段階では、ファイル作成と class 名変更のみ // この後の作業で書き換えます import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; @NgModule({ declarations: [], imports: [CommonModule], }) export class RoutesModule {}
■ ページ遷移用のコンポーネントを作成します
# 親コンポーネント生成 $ npx ng g c parent --standalone CREATE src/app/parent/parent.component.css (0 bytes) CREATE src/app/parent/parent.component.html (21 bytes) CREATE src/app/parent/parent.component.spec.ts (594 bytes) CREATE src/app/parent/parent.component.ts (297 bytes) # 子コンポーネント生成 $ npx ng g c child --standalone CREATE src/app/child/child.component.css (0 bytes) CREATE src/app/child/child.component.html (20 bytes) CREATE src/app/child/child.component.spec.ts (587 bytes) CREATE src/app/child/child.component.ts (293 bytes) # 孫コンポーネント生成 $ npx ng g c grand-child --standalone CREATE src/app/grand-child/grand-child.component.css (0 bytes) CREATE src/app/grand-child/grand-child.component.html (26 bytes) CREATE src/app/grand-child/grand-child.component.spec.ts (623 bytes) CREATE src/app/grand-child/grand-child.component.ts (316 bytes)
■ 上記手順のディレクトリ構成
$ cd ../.. $ tree . ├── LICENSE ├── README.md ├── angular.json ├── package-lock.json ├── package.json ├── src │ ├── app │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.config.ts # ルーティング環境設定 │ │ ├── app.routes.ts # ルーティング │ │ ├── child # 子コンポーネント │ │ │ ├── child.component.css │ │ │ ├── child.component.html │ │ │ ├── child.component.spec.ts │ │ │ └── child.component.ts │ │ ├── grand-child # 孫コンポーネント │ │ │ ├── grand-child.component.css │ │ │ ├── grand-child.component.html │ │ │ ├── grand-child.component.spec.ts │ │ │ └── grand-child.component.ts │ │ ├── list │ │ └── parent # 親コンポーネント │ │ ├── parent.component.css │ │ ├── parent.component.html │ │ ├── parent.component.spec.ts │ │ └── parent.component.ts │ ├── assets │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ └── styles.css ├── tsconfig.app.json ├── tsconfig.json └── tsconfig.spec.json
ルーターの実装
■ ルーター関連の実装
- app.config.ts
import { ApplicationConfig } from '@angular/platform-browser'; // main.ts のインポート文をこちらに移植 import { provideHttpClient } from '@angular/common/http'; import { provideRouter, withDebugTracing, withHashLocation, withInMemoryScrolling, } from '@angular/router'; import { routes } from './app.routes'; export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(), // main.ts の providers セクションのコードをこちらに移植 provideRouter( routes, // 複数のオプションを同時に指定する withHashLocation(), withDebugTracing(), withInMemoryScrolling() ), ], };
- app.route.ts
import { Routes } from '@angular/router'; import { ParentComponent } from './parent/parent.component'; export const routes: Routes = [ { path: '', // 通常の読み込み component: ParentComponent, }, { path: 'parent', // 単一の遅延読み込み loadComponent: () => import('./parent/parent.component').then((m) => m.ParentComponent), }, ];
- main.ts
import { bootstrapApplication } from '@angular/platform-browser'; - import { provideHttpClient } from '@angular/common/http'; import { AppComponent } from './app/app.component'; + import { appConfig } from './app/app.config'; // providers: [provideHttpClient()], は、app.config.ts へ移動 // ApplicationConfig で、関連する DI を一纏めにする為 - bootstrapApplication(AppComponent, { - providers: [provideHttpClient()], - }).catch((error) => console.error(error)); + bootstrapApplication(AppComponent, appConfig).catch((error) => + console.error(error) + );
■ 画面関係の実装変更
- app.component.ts
import 'zone.js/dist/zone.js'; import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; + import { RouterOutlet } from '@angular/router'; import { ListComponent } from './list/list.component'; @Component({ selector: 'app-root', standalone: true, - imports: [CommonModule, ListComponent], + imports: [CommonModule, RouterOutlet, ListComponent], templateUrl: './app.component.html', styleUrls: ['./app.component.css'], }) export class AppComponent { title = 'stand alone components sample'; }
- app.component.html
<h1>{{ title }}</h1> - <app-list></app-list> + <router-outlet></router-outlet>
- parent.component.html
<p>parent works!</p>
+ <app-list></app-list>
- parent.component.ts
import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; + import { ListComponent } from '../list/list.component'; @Component({ selector: 'app-parent', standalone: true, - imports: [CommonModule], + imports: [CommonModule, ListComponent], templateUrl: './parent.component.html', styleUrls: ['./parent.component.css'], }) export class ParentComponent {}
■ ポイント
- main.ts の providers は、ApplicationConfig する時は、そちらで実装する。
- ApplicationConfig を使用すると bootstrapApplication の実装が簡素化される
階層ルーティング
■ 子・孫コンポーネント用ルーティングファイル作成
$ cd src/app # 親 route module生成(routeファイルのディレクトリ移動、ファイル名変更、移動元ディレクトリ削除) $ npx ng g m parent.routes && mv ./parent.routes/parent.routes.module.ts ./parent.routes.ts && rm -rf ./parent.routes CREATE src/app/parent.routes/parent.routes.module.ts (198 bytes) # 子 route module生成(routeファイルのディレクトリ移動、ファイル名変更、移動元ディレクトリ削除) $ npx ng g m child.routes && mv ./child.routes/child.routes.module.ts ./child.routes.ts && rm -rf ./child.routes CREATE src/app/child.routes/child.routes.module.ts (197 bytes) $ ../.. $ tree . ├── LICENSE ├── README.md ├── angular.json ├── package-lock.json ├── package.json ├── src │ ├── app │ │ ├── app.component.css │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.config.ts │ │ ├── app.routes.ts │ │ ├── child │ │ ├── child.routes.ts # 子コンポーネント用ルーティング │ │ ├── grand-child │ │ ├── list │ │ ├── parent │ │ └── parent.routes.ts # 親コンポーネント用ルーティング │ ├── assets │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ └── styles.css ├── tree.txt ├── tsconfig.app.json ├── tsconfig.json └── tsconfig.spec.json
■ 階層ルーティングを実装
- app.routes.ts
import { Routes } from '@angular/router'; - import { ParentComponent } from './parent/parent.component'; - export const routes: Routes = [ - { - path: '', - // 通常の読み込み - component: ParentComponent, - }, - { - path: 'parent', - // 単一の遅延読み込み - loadComponent: () => - import('./parent/parent.component').then((m) => m.ParentComponent), - }, - ]; // 下の階層の routes.ts をロード + export const routes: Routes = [ + { + path: '', + loadChildren: () => import('./parent.routes').then((m) => m.parentRoutes), + }, + ];
- parent.routes.ts
import { Routes } from '@angular/router'; export const parentRoutes: Routes = [ { path: '', // 自身(ParentComponent)のコンポーネント をロード loadComponent: () => import('./parent/parent.component').then((m) => m.ParentComponent), // 下の階層の routes.ts をロード children: [ { path: '', loadChildren: () => import('./child.routes').then((mod) => mod.childRoutes), }, ], }, ];
- child.routes.ts
import { Routes } from '@angular/router'; export const childRoutes: Routes = [ { path: '', // 自身(ChildComponent)のコンポーネント をロード loadComponent: () => import('./child/child.component').then((m) => m.ChildComponent), // 下の階層のコンポーネント をロード children: [ { path: '', loadComponent: () => import('./grand-child/grand-child.component').then( (m) => m.GrandChildComponent ), }, ], }, ];
■ parent コンポーネントとスタイル実装
- parent.component.ts
import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; + import { RouterOutlet } from '@angular/router'; import { ListComponent } from '../list/list.component'; @Component({ selector: 'app-parent', standalone: true, - imports: [CommonModule, ListComponent], + imports: [CommonModule, RouterOutlet, ListComponent], templateUrl: './parent.component.html', styleUrls: ['./parent.component.css'], }) export class ParentComponent {}
- parent.component.css
div { border: 2px solid red; margin: 5px; padding: 5px; }
- parent.component.html
<div> <p>parent works!</p> <app-list></app-list> <router-outlet></router-outlet> </div>
■ child コンポーネントとスタイル実装
- child.component.ts
import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; + import { RouterOutlet } from '@angular/router'; @Component({ selector: 'app-child', standalone: true, - imports: [CommonModule], + imports: [CommonModule, RouterOutlet], templateUrl: './child.component.html', styleUrls: ['./child.component.css'], }) export class ChildComponent {}
- child.component.css
div { border: 2px solid blue; margin: 5px; padding: 5px; }
- child.component.html
<div> <p>child works!</p> <router-outlet></router-outlet> </div>
■ grand-child コンポーネントとスタイル実装
- grand-child.component.css
div { border: 2px solid orange; margin: 5px; padding: 5px; }
- grand-child.component.html
<div> <p>grand-child works!</p> </div>
■ 実行結果イメージ
まとめ
- main.ts の providers は、ApplicationConfig する時は、そちらで実装する。
- ApplicationConfig を使用すると bootstrapApplication の実装が簡素化される
- ディレクティブの合成 のキャッチアップは次回に続きます。
関連記事
- 実践 Angular: Standalone Components をざっくりキャッチアップしてみた Part1
- 実践 Angular: Standalone Components をざっくりキャッチアップしてみた Part2
- 実践 Angular: Standalone Components をざっくりキャッチアップしてみた Part3
- 実践 Angular: Standalone Components をざっくりキャッチアップしてみた Part4
- 実践 Angular: Standalone Components をざっくりキャッチアップしてみた Part5
- 実践 Angular: Standalone Components をざっくりキャッチアップしてみた Part6(まとめ)
参考文献