← 解説「linkedSignal — 連動して作り直せる書き込み可能な状態」に戻る
サンプルコードで身につける: linkedSignal — 連動して作り直せる書き込み可能な状態
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1signal・computed・linkedSignal の位置づけ
3つの道具の違いは「値がどこから来るか」と「上書きできるか」で整理できます。linkedSignalは両方の性質を持つ中間の存在です。この表を頭に入れると使い分けに迷いません。
// ── 3つの状態の道具の使い分け ──
//
// 値の出どころ set()で上書き
// signal 自分で入れる できる
// computed 他のシグナルから計算 できない
// linkedSignal 他のシグナルから計算 できる ★中間
//
// linkedSignal = 「基本は計算で決まるが、
// 依存が変わるまでは上書きが生きる」状態
2リストが変わると選択がリセットされる流れ
カテゴリを切り替えると商品リストが変わり、選択中の商品が自動で先頭に戻ります。ユーザーが選び直した値は、次にリストが変わるまで保持されます。この一連の動きをコメントで追ってみます。
import { signal, linkedSignal } from '@angular/core';
const items = signal(['りんご', 'みかん']);
const selected = linkedSignal(() => items()[0]);
console.log(selected()); // 'りんご'(計算結果)
selected.set('みかん');
console.log(selected()); // 'みかん'(上書きが生きている)
items.set(['牛乳', 'パン']); // 依存が変わった!
console.log(selected()); // '牛乳'(計算し直されてリセット)
3source と computation を分けた書き方
linkedSignalにはオブジェクト形式もあり、監視する source と計算の computation を分けて書けます。computation は前回の値も受け取れるので、「新しいリストにも前の選択が残っていれば維持する」といった賢いリセットが書けます。
import { signal, linkedSignal } from '@angular/core';
const items = signal(['りんご', 'みかん', 'ぶどう']);
const selected = linkedSignal({
source: items,
computation: (newItems, previous) => {
// 前回の選択が新リストにも入っていれば、それを維持
if (previous && newItems.includes(previous.value)) {
return previous.value;
}
return newItems[0]; // 無ければ先頭にリセット
},
});
selected.set('みかん');
items.set(['みかん', 'バナナ']); // みかんは残っている
console.log(selected()); // 'みかん'(維持された)
4effect で書くとアンチパターンになる例
linkedSignalが無い時代は、signalとeffectを組み合わせて同じ動きを作っていました。状態の定義とリセットのロジックが離れてしまい、追いかけにくいコードになります。Before/Afterで比べてみましょう。
// ── Before: signal + effect(追いにくい)──
// selected = signal<string | undefined>(undefined);
// constructor() {
// effect(() => {
// // リセットの理由がここに埋もれる。書き込み経路も2本になる
// this.selected.set(this.items()[0]);
// });
// }
// ── After: linkedSignal(1行で意図が見える)──
// selected = linkedSignal(() => this.items()[0]);
//
// 「itemsが変わったら先頭に戻る、それ以外は上書きが生きる」
// という仕様が、定義の1行だけで読み取れる
5編集フォームの下書きに使う
「選択中のユーザーが変わったら入力欄を元データに戻す。ただし編集中の値は保持する」という編集画面の定番要件も、linkedSignalそのものです。resource()やhttpResource()の取得結果と組み合わせても同じ形になります。
import { Component, signal, linkedSignal } from '@angular/core';
type User = { id: number; name: string };
@Component({
selector: 'app-user-editor',
template:
'<input [value]="draftName()" ' +
' (input)="draftName.set($any($event.target).value)">',
})
export class UserEditorComponent {
user = signal<User>({ id: 1, name: '松本' });
// 編集用の下書き。user が別人に変わったら元データに戻る
draftName = linkedSignal(() => this.user().name);
save() {
console.log('保存する名前:', this.draftName());
}
}