Dev Study
← 解説「NgModule — スタンドアロン以前のアプリの骨組み」に戻る

サンプルコードで身につける: NgModule — スタンドアロン以前のアプリの骨組み

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

14つの配列の役割マップ

NgModuleを読むときに見る場所は実質4つだけです。それぞれの配列が何を意味するかを一枚にまとめます。この地図を持っていれば、大きなモジュールでも迷いません。

// ── @NgModule の読み方マップ ──
//
// declarations: [...]  所有する部品の一覧
//   → 「このコンポーネントはどこの所属?」の答えがここ
//
// imports: [...]       借りてくる機能
//   → テンプレートで使える部品の供給源。
//     CommonModule(*ngIf等) / FormsModule(ngModel) が頻出
//
// providers: [...]     DIへのサービス登録
//   → 現代は providedIn: 'root' に書く内容の旧居
//
// exports: [...]       他モジュールへの貸し出し
//   → ここに無い部品は、他モジュールからは使えない

2「この部品どこから来た?」の追跡手順

テンプレートに知らないコンポーネントが出てきたときの、NgModule時代の探し方です。この追跡ルートは移行中の混在コードでもそのまま使えます。

// テンプレートに <app-user-badge> が出てきた。定義はどこ?
//
// 1. いま読んでいるコンポーネントが standalone か確認
//    → standalone なら自分の imports 配列を見る(現代の読み方)
//
// 2. standalone でなければ、自分を declarations している
//    モジュール(UserModule など)を探す
//    → エディタで 'UserListComponent' を全文検索すると早い
//
// 3. そのモジュールの declarations に UserBadgeComponent があるか
//    → あれば同じモジュール所属。定義ファイルへジャンプ
//
// 4. 無ければ imports のモジュールを順に見て、
//    exports に UserBadgeComponent を出しているものを探す

3SharedModule と FeatureModule という定番構成

NgModule時代のプロジェクトには決まった構成パターンがありました。名前の付き方に意味があるので、フォルダ構成を見た瞬間に役割を推測できるようになります。

// ── NgModule 時代の典型的な構成 ──
//
// AppModule          起動の起点。bootstrap: [AppComponent] を持つ
//
// CoreModule         アプリで1回だけ読み込むもの
//                    (認証サービス・共通ヘッダーなど)
//
// SharedModule       どこでも使う汎用部品の詰め合わせ。
//                    ボタンやパイプを declarations して
//                    そのまま exports で貸し出す
//
// UserModule など    機能(feature)単位のモジュール。
// (FeatureModule)    自分のルーティング定義を持つことが多い
//
// → 実務スキル編で学んだ「feature単位のディレクトリ構成」は
//   この構成の考え方を standalone 時代に引き継いだもの

4NgModule と standalone の混在コードを読む

移行中のプロジェクトでは両方が同居します。NgModuleがstandalone部品をimportsで借りる形と、standalone部品がモジュールをimportsする形、どちらも合法です。

// パターン1: NgModule が standalone 部品を借りる
@NgModule({
  declarations: [LegacyPageComponent], // 旧来の部品
  imports: [
    CommonModule,
    UserBadgeComponent, // ← standalone コンポーネントを直接 imports
  ],
})
export class LegacyModule {}

// パターン2: standalone 部品が NgModule を借りる
@Component({
  selector: 'app-new-page',
  imports: [SharedModule], // ← モジュールごと借りて中の部品を使う
  template: '...',
})
export class NewPageComponent {}

5bootstrapModule — 起動コードの新旧

main.tsの起動コードも時代で形が違います。既存プロジェクトのmain.tsを見れば、そのプロジェクトがどちらの世代かを一目で判定できます。

// ── 旧: NgModule 時代の main.ts ──
// import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
// import { AppModule } from './app/app.module';
//
// platformBrowserDynamic().bootstrapModule(AppModule);
//   → AppModule の bootstrap: [AppComponent] から起動

// ── 新: standalone 時代の main.ts ──
// import { bootstrapApplication } from '@angular/platform-browser';
// import { AppComponent } from './app/app.component';
// import { appConfig } from './app/app.config';
//
// bootstrapApplication(AppComponent, appConfig);
//   → モジュールを介さずコンポーネントを直接起動。
//     providers は app.config.ts に集まる