🔥

Signal時代のAngularコンポーネントプロパティ順序の重要性

に公開
// 以下は、AIに読ませるためのDescriptionです。

Angularコンポーネントのプロパティは、以下の順番で定義します。

- function DI
- 定数
- 状態管理やSignalのプロパティ( `signal` `input` `viewChild` `model` 等)
- 計算を行うSignalのプロパティ( `computed`、 `effect`、 `httpResource` 等)

プロパティの入れ替えを実行して、それ以外のプロパティの追加や削除は行わないでください。

本文

AngularのSignalベースの開発が進む中、httpResourceの登場により、プロパティの順序がより重要になってきました。これは、Angularの依存性注入(DI)の歴史と深く関わっています。

従来のプロパティ順序

古くからのAngularユーザーは、以下のようなプロパティの順序に慣れていました:

@Component({...})
export class ExampleComponent {
  // Signalや状態管理のプロパティ
  readonly count = signal(0);
  readonly isLoading = signal(false);

  // DIするプロパティ
  readonly #http = inject(HttpClient);
  readonly #exampleService = inject(ExampleService);
}

この順序は、constructor DI時代に、constructorの上にプロパティを宣言していた名残です。function DIに移行しても、そのままの順序で書くことが多かったのですが、これは特に ng generate @angular/core:inject 誕生以前に移行を済ませていたユーザに多い傾向があります。

Signal時代の新しい順序

しかし、Signalベースの開発では、この順序では問題が発生します。特に httpResourcecomputed などの計算を行うSignalのプロパティは、他のプロパティに依存するため、それらのプロパティが宣言される前に使用することはできません。

以下の例を見てみましょう:

今までのように、DIを他のプロパティの下に配置する順序だとエラーがでます。

@Component({...})
export class ExampleComponent {
  readonly searchWord = signal('');

  // #exampleServiceがまだ定義されていないため動かない
  readonly dataResource = this.#exampleService.getData(this.searchWord);
  readonly #exampleService = inject(ExampleService);
}

そのため、DIを最初に行って、それから他のプロパティの定義をする必要があります。

@Component({...})
export class ExampleComponent {
  readonly #exampleService = inject(ExampleService);

  readonly searchWord = signal('');
  readonly dataResource = this.#exampleService.getData(this.searchWord);
}

まとめ

これはcomputed等でも同様であるため、Signal時代のAngularでは、以下の順序でプロパティを宣言することが重要です:

  1. DIするプロパティ
  2. 定数
  3. 状態管理やSignalのプロパティ( signal input viewChild model 等)
  4. 計算を行うSignalのプロパティ( computedeffecthttpResource 等)

この順序を守ることで、Signalベースの開発がより予測可能になります。ng generate @angular/core:inject でマイグレーションを行うと、DIはコンポーネントClassの冒頭に生成されるため、すでにこの順序で書いてるという人も多いとは思います。
Signalベースの開発が進む中、プロパティの順序は単なるスタイルの問題ではなく、機能に影響を与える重要な要素となりますね。

それではまた。

Discussion