Skip to content

Angular 19におけるAPP_INITIALIZERの非推奨と移行方法

問題点

Angular 18から19へアップグレードする際、APP_INITIALIZERの使用が非推奨となり、新しいprovideAppInitializer関数への移行が必要です。しかし自動マイグレーションで変換されたコードを実行すると、次のエラーが発生します:

error
Uncaught RuntimeError: NG0203: inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with `runInInjectionContext`.

この問題は主に、inject()を適切な依存性注入コンテキストで呼び出していないことが原因です。Angular 19では初期化ロジックの登録方法が変更されましたが、自動移行ツールが完全に正しいコードを生成しないケースがあります。

解決方法

🛠 方法1: 初期化関数のラッピング(手動修正)

エラーを解決する最も直接的な方法は、初期化関数をラムダ(アロー関数)でラップしinject()の呼び出しを「依存性注入コンテキスト」内で実行することです:

typescript
// 修正後のコード
providers: [
  provideAppInitializer(() => initializeApp1(inject(AuthService)))
]

重要なポイント

  • () =>で関数をラップすることで安全な注入コンテキストを生成
  • inject(Service)で既存サービスのインスタンスを注入可
  • multi: trueオプションは不要(自動管理)

実装パターン例

typescript
const initializeApp = () => {
  const configService = inject(ConfigService);
  return configService.loadConfig();
};

// プロバイダ登録
providers: [provideAppInitializer(initializeApp)];

⚙️ 方法2: 公式マイグレーションコマンドの実行(推奨)

Angular CLIが提供する専用マイグレーションコマンドで完全な修正を行います:

bash
ng update @angular/core --name provide-initializer

アップデート中に以下のプロンプトが表示された場合:

** Optional migrations of package '@angular/core' **

This package has 1 optional migration:
❯ Replaces APP_INITIALIZER, ENVIRONMENT_INITIALIZER & PLATFORM_INITIALIZER 
  respectively with provideAppInitializer, provideEnvironmentInitializer & providePlatformInitializer.

このコマンドがAPP_INITIALIZERを適切な形式のprovideAppInitializerに自動変換します。

注意

  • CLIの初期アップグレード(ng update @angular/core@19)時には同時に実行されない
  • 明示的に追加コマンドを実行する必要あり

🔧 複雑な依存関係の対応例

複数の依存関係が必要なケースや初期化ロジックが複雑な場合は関数をネストします:

typescript
provideAppInitializer(() => {
  const authService = inject(AuthService);
  const configService = inject(ConfigService);
  
  return () => {
    return authService.init().pipe(
      switchMap(() => configService.load())
    );
  };
}),

エラーの根本原因

NG0203エラーはinject()関数が不適切なコンテキストで呼び出された場合に発生します。Angular 19のprovideAppInitializerは:

  1. 初期化関数を依存性注入コンテキスト内で実行
  2. 無名関数ではない既存のファクトリ関数を直接渡すとコンテキスト喪失
  3. 関数ラッピングがコンテキスト境界を確立

元のAPP_INITIALIZERdepsオプションは不要に:

diff
- { provide: APP_INITIALIZER, useFactory: ... }
+ provideAppInitializer(...) // 簡潔な関数ベースAPI
内部構造の変更点(Advanced)
Angular 18Angular 19
APP_INITIALIZERトークンprovideAppInitializer()関数
multi: true管理内部で複数登録自動対応
明示的deps指定inject()による暗黙的解決

ベストプラクティス

  1. 公式マイグレーションを優先
    ng update --name provide-initializerを最初に実行

  2. 初期化関数の軽量化

    typescript
    // ❌ 重い処理
    provideAppInitializer(() => fetchBigData()) 
    
    // ✅ 最小限に保つ
    provideAppInitializer(() => initializeEssentialConfig())
  3. 非同期処理の扱い
    関数がPromiseを返す場合、Angularはその解決を待機:

    typescript
    provideAppInitializer(async () => {
      await inject(ConfigLoader).fetch();
    })
  4. Environment Initializerの利用検討
    アプリ起動前初期化が必要な場合は:
    provideEnvironmentInitializer()provideAppInitializerを使い分け

移行後の検証手順

  1. ng serve実行後、コンソールエラーをチェック
  2. 初期化処理が正しく実行されているかログで確認
  3. 依存サービスがundefinedになっていないかテスト

Angular 19では初期化メカニズムがシンプルかつ型安全になりました。適切な移行によりアプリケーション起動プロセスの信頼性が向上します。