Angularのmoduleでネストしたルーティングをやってみた

2022/07/21に公開

やりたかったこと

/adminのように、特定のパス以下でのルーティングやファイル群をmoduleにまとめたい。

ベースアプリ

公式チュートリアルをやっていたので、これをリファクタリングした。

https://angular.jp/tutorial

やったこと

1: 子ページのRoutingモジュールを作成

子ページにしたいコンポーネントのインポートとRouteの定義を実施。
ネスト先の場合、RouterModuleforChildを使う。

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HeroesComponent } from './heroes/heroes.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
import { DashboardComponent } from './dashboard/dashboard.component';

const heroesRoutes: Routes = [
  {
    path: '',
    component: HeroesComponent,
  },
  {
    path: 'dashboard',
    component: DashboardComponent,
  },
  {
    path: ':id',
    component: HeroDetailComponent,
  },
];

@NgModule({
  declarations: [],
  imports: [RouterModule.forChild(heroesRoutes)],
  exports: [RouterModule],
})
export class HeroesRoutingModule {}

2: 子ページのmoduleも作っておく

moduleの中身もapp.module.tsから移動させる。
HeroServiceprovidersに追加しないと、AppModulesからHttpClientModuleを剥がせなかった。

import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
import { InMemoryDataService } from '../in-memory-data.service';
import { DashboardComponent } from './dashboard/dashboard.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
import { HeroSearchComponent } from './hero-search/hero-search.component';
import { HeroesRoutingModule } from './heroes-routing.module';
import { HeroService } from './heroes/hero.service';
import { HeroesComponent } from './heroes/heroes.component';

@NgModule({
  declarations: [
    HeroesComponent,
    HeroDetailComponent,
    HeroSearchComponent,
    DashboardComponent,
  ],
  imports: [
    CommonModule,
    FormsModule,
    HeroesRoutingModule,
    HttpClientModule,
    HttpClientInMemoryWebApiModule.forRoot(InMemoryDataService, {
      dataEncapsulation: false,
    }),
  ],
  exports: [
    HeroesComponent,
    HeroDetailComponent,
    HeroSearchComponent,
    DashboardComponent,
  ],
  providers: [HeroService],
})
export class HeroesModule {}

3: app-routing.module.tsで子ページのモジュールを読み込む

読み込みたいパスで、loadChildrenを使ってモジュールをimportする。
loadChildrenで読み込むファイルパスを文字列で指定する記事もありますが、今回のAngular v14以降ではDynamic importでやる必要がありました。

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  {
    path: 'heroes',
    loadChildren: () =>
      import('./heroes/heroes.module').then((m) => m.HeroesModule),
  },
  {
    path: '',
    redirectTo: '/heroes/dashboard',
    pathMatch: 'full',
  },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

4: app.modules.tsを整理

移動させたモジュールやコンポーネントを消します。
ここで子ページのモジュールをimportするとバグるので要注意。

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { MessagesComponent } from './messages/messages.component';

@NgModule({
  declarations: [AppComponent, MessagesComponent],
  imports: [BrowserModule, AppRoutingModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

感想

後からモジュール化したのでちょっと手間でしたが、先にモジュールがある状態ならばCLIでのgenereteでよしなにやってくれるのではと期待しています。

実際にやる場合は、Serviceについても別Moduleにしておいた方が使いやすそうかも?

参考

https://qiita.com/Yamamoto0525/items/e870713d9d05d2d36a80

https://github.com/Yamamoto0525/NgChat/tree/682b504fb8cb8f3294fc66d55f25420a432c55cd

Discussion