← 解説「computed — シグナルから作る派生値」に戻る
サンプルコードで身につける: computed — シグナルから作る派生値
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
11つのシグナルから2倍の値を作る
computed の最小例で、count が変わるたびに doubled も自動で新しい値になります。doubled 自身は set できない読み取り専用で、値の出どころが count だけに限定されるのがポイントです。「元の値と派生値」という関係の出発点です。
import { Component, computed, signal } from '@angular/core';
@Component({
selector: 'app-double-view',
template:
'<p>{{ count() }} の2倍は {{ doubled() }}</p>' +
'<button (click)="add()">+1</button>',
})
export class DoubleViewComponent {
count = signal(1);
// count が変わると doubled も自動で変わる(読み取り専用)
doubled = computed(() => this.count() * 2);
add() {
this.count.update((v) => v + 1);
}
}
2複数のシグナルから合計金額を導く
computed は複数のシグナルに依存でき、どれか1つでも変われば再計算されます。単価と個数から合計を導く、というカートの定番計算で「合計を自分で更新するコードがどこにもない」ことに注目してください。更新するのは元の値だけ、が computed 設計の鉄則です。
import { Component, computed, signal } from '@angular/core';
@Component({
selector: 'app-cart-total',
template:
'<p>単価 {{ unitPrice() }} 円 × {{ quantity() }} 個</p>' +
'<p>合計: {{ total() }} 円</p>' +
'<button (click)="addOne()">1個追加</button>',
})
export class CartTotalComponent {
unitPrice = signal(300);
quantity = signal(2);
// どちらが変わっても合計は自動で再計算される
total = computed(() => this.unitPrice() * this.quantity());
addOne() {
this.quantity.update((v) => v + 1);
}
}
3computed の仕組みを自作して動かす
「依存するシグナルの変更を購読して、計算結果を作り直す」という computed の核を、プレーンなTypeScriptで体験する例です。quantity を書き換えただけで total の読み出し結果が変わる様子を、実際に実行して確認できます。派生値を手で更新しなくてよい理由がこの数行に詰まっています。
TypeScript
4送信可能かどうかを派生値にする
「入力が空でなければ送信できる」という真偽値も、元の入力シグナルから導ける立派な派生値です。canSubmit をボタンの [disabled] に直結することで、入力のたびにボタンの状態が自動で切り替わります。フォームのバリデーション表示は computed の代表的な実務ユースケースです。
import { Component, computed, signal } from '@angular/core';
@Component({
selector: 'app-feedback-form',
template:
'<input (input)="onInput($event)" placeholder="ご意見をどうぞ">' +
'<button [disabled]="!canSubmit()">送信</button>',
})
export class FeedbackFormComponent {
comment = signal('');
// 「空でないか」という真偽値も派生値として導ける
canSubmit = computed(() => this.comment().trim().length > 0);
onInput(e: Event) {
this.comment.set((e.target as HTMLInputElement).value);
}
}
5キーワードで一覧を絞り込む
「絞り込み後の一覧」を computed で持つ、実務の検索・フィルタUIの定番構成です。キーワードと元データのどちらが変わっても一覧が自動で作り直され、絞り込みロジックはこの1箇所に集約されます。@for にはこの computed をそのまま渡すだけで済みます。
import { Component, computed, signal } from '@angular/core';
@Component({
selector: 'app-fruit-filter',
template:
'<input (input)="onInput($event)" placeholder="名前で絞り込み">' +
'<ul>@for (fruit of filtered(); track fruit) { <li>{{ fruit }}</li> }</ul>',
})
export class FruitFilterComponent {
keyword = signal('');
fruits = signal(['りんご', 'りんごジュース', 'バナナ', 'みかん']);
// 元データとキーワードのどちらが変わっても自動で作り直される
filtered = computed(() =>
this.fruits().filter((f) => f.includes(this.keyword())),
);
onInput(e: Event) {
this.keyword.set((e.target as HTMLInputElement).value);
}
}