← 解説「エラーメッセージの解剖 — NGコードとスタックトレース」に戻る
サンプルコードで身につける: エラーメッセージの解剖 — NGコードとスタックトレース
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1NG0201: provider が見つからない
NG02xx はDIまわりのエラーです。HttpClient を inject しているのに provideHttpClient の登録を忘れると NG0201 が出ます。エラーコードから「注入の設定漏れ」と当たりを付けられれば、直す場所は app.config.ts だと分かります。
// ERROR RuntimeError: NG0201: No provider found for _HttpClient!
import { Component, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-user-list',
template: '<p>ユーザー一覧</p>',
})
export class UserList {
// 原因: HttpClient を注入しているが、アプリに提供元が未登録
private http = inject(HttpClient);
}
// 直し方: app.config.ts に provideHttpClient() を追加する
//
// export const appConfig: ApplicationConfig = {
// providers: [provideHttpClient()], // ← これを足す
// };
2NG0100: 変更検知後に値が変わった
NG01xx はテンプレート・変更検知まわりのエラーです。描画が終わった直後のフックで値を書き換えると NG0100 が出ます。シグナルを使い、値の更新をAngularに正しく伝えるのが現代的な直し方です。
// ERROR RuntimeError: NG0100: ExpressionChangedAfterItHasBeenCheckedError
import { AfterViewInit, Component, signal } from '@angular/core';
@Component({
selector: 'app-header',
template: '<h1>{{ title }}</h1>',
})
export class Header implements AfterViewInit {
title = '読み込み中';
ngAfterViewInit() {
// 原因: 描画チェックが終わった直後に、ただのプロパティを書き換えた
// Angularは「検査後に値が変わった」ことを開発モードで検出して怒る
this.title = 'ダッシュボード';
}
}
// 直し方: シグナルにすると更新が正しく変更検知に伝わる
// title = signal('読み込み中');
// ngAfterViewInit() { this.title.set('ダッシュボード'); }
// テンプレートは {{ title() }} にする
3NG0200: サービス同士の循環依存
サービスAがBを、BがAを注入し合うと NG0200 が出ます。エラーコードだけで「依存の輪ができている」と分かるのがNGコードの便利なところです。共通部分を第三のサービスに切り出して輪を断ち切ります。
// ERROR RuntimeError: NG0200: Circular dependency detected for CartService
import { Injectable, inject } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class CartService {
// 原因その1: CartService が StockService に依存し…
private stock = inject(StockService);
}
@Injectable({ providedIn: 'root' })
export class StockService {
// 原因その2: StockService も CartService に依存している(輪が完成)
private cart = inject(CartService);
}
// 直し方: 両者が必要とする共通処理を第三のサービスに切り出し、
// 依存を CartService → 共通 ← StockService の一方向にする
4スタックトレースから自分の行を探す
スタックトレースの大半は Angular 内部のファイルで、読む必要はありません。src/app 配下のファイル名が出ている行だけを探し、そこを調査の起点にします。上にある行ほどエラー発生地点に近いことも覚えておきましょう。
// ERROR TypeError: Cannot read properties of undefined (reading 'name')
//
// at UserCard_Template (user-card.html:3) ← ②テンプレート3行目
// at executeTemplate (core.mjs:11249) ← Angular内部(読み飛ばす)
// at refreshView (core.mjs:12780) ← Angular内部(読み飛ばす)
// at detectChangesInView (core.mjs:13004) ← Angular内部(読み飛ばす)
// at UserList.loadUsers (user-list.ts:24) ← ①自分のファイル!
//
// 読み方:
// 1. src/app 配下のファイル名(user-card.html / user-list.ts)だけ探す
// 2. 上の行ほどエラー発生地点に近い
// → user-card.html の3行目で undefined の name を読もうとした
// 3. 「なぜ undefined だったか」を user-list.ts:24 に遡って調べる
//
// この例の典型的な直し方: データ到着前の描画に備えて
// @if (user(); as u) { {{ u.name }} } のように守る
5エラーコードで公式解説を引く
NGコードは angular.dev/errors の該当ページに直結しています。エラー文全体で検索するより、コードをURLに付けて公式解説を開くほうが速くて正確です。解説には典型的な原因と直し方が載っています。
// エラーコードから公式解説へたどる手順
//
// コンソールの表示:
// ERROR RuntimeError: NG0203: inject() must be called from
// an injection context ...
//
// 1. コード部分(NG0203)だけを抜き出す
// 2. URLに付けて開く:
// https://angular.dev/errors/NG0203
// 3. 解説ページで分かること:
// ・意味: inject() を注入コンテキスト外で呼んだ
// ・典型例: クラスの外や setTimeout の中で inject() を呼んだ
// ・直し方: フィールド初期化子かコンストラクタで呼ぶ
//
// 悪い例(NG0203 が出る):
// onClick() { const http = inject(HttpClient); } // メソッド内はダメ
//
// 直した例:
// private http = inject(HttpClient); // フィールド初期化子ならOK