Angularでのコンポーネント内からの入力シグナル値変更
問題点:
Angularのinput()
で宣言された入力シグナルに対して、コンポーネント内で.set()
や.update()
を直接呼び出そうとしても動作しません。これは以下のエラーが発生する典型的なケースです:
typescript
// コンポーネント内での試行(エラー発生)
this.selected.set(true); // Property 'set' does not exist
this.selected().set(true); // 実行時エラー
根本原因:
Angularの入力シグナル(input()
)は設計上読み取り専用(read-only) です。親コンポーネントから受け取った値のみを反映し、子コンポーネント側からの変更は意図的に禁止されています。公式RFCでも明示されています:
"Inputs are read-only"(入力は読み取り専用)
Angular Signal RFC Discussion
適切な解決策
方法1: model()
を使用した双方向バインディング(推奨)
Angular v17.2以降では、書き込み可能な双方向バインディング向けのmodel()
APIが提供されています:
typescript
import { Component, model } from '@angular/core';
@Component({...})
export class YourComponent {
// model()で宣言(初期値false)
selected = model(false);
toggleSelection() {
// コンポーネント内での更新が可能
this.selected.set(!this.selected());
}
}
親コンポーネントでの使用例:
html
<your-component [(selected)]="parentValue" />
typescript
parentValue = signal(false); // 自動的に同期される
特徴:
model()
は書き込み可能なシグナルを生成[(selected)]
構文で双方向バインディングを実現- 値変更が親コンポーネントに自動通知される
方法2: computed()
を使用した派生状態(読み取り専用が必要な場合)
直接変更できず単なる表示用の値が必要な場合の代替案:
typescript
import { computed, input } from '@angular/core';
selected = input(false);
private _localSelected = signal(false);
// 入力値と内部状態を合成
hasSelected = computed(() =>
this.selected() || this._localSelected()
);
ユースケース:
- 入力値を基にした読み取り専用の派生値が必要な場合
- 内部状態と入力値を組み合わせたいとき
なぜ input()
は書き込み不可なのか?
データフローの明確化:
親→子の一方通行を強制し、予期しない副作用を防止変更検知の最適化:
入力値の変更源を常に親コンポーネントに限定することでパフォーマンス向上デバッグ容易性:
データ変更が常に親コンポーネント経由で発生するためトレースが容易
実運用でのアドバイス
- 双方向バインディングが必要な場合: 常に
model()
を選択 - 単なる入力値の受け取りだけ:
input()
が適切 - Angularバージョン: v17.2未満では
computed()
を一時的に使用し、早期バージョンアップを推奨
関連リソース: