Dev Study
← 解説「@let — テンプレート内にローカル変数を作る」に戻る

サンプルコードで身につける: @let — テンプレート内にローカル変数を作る

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1計算式に名前を付ける

@let のいちばん基本の形で、税込価格の計算に taxed という名前を付けています。同じ式を2回書かずに済み、元の price() が変われば @let の結果も自動で追従します。

import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-price-tag',
  // 計算結果に taxed という名前を付けて使い回す
  template:
    '@let taxed = price() * 1.1;' +
    '<p>税込 {{ taxed }} 円</p>' +
    '<p>2個なら {{ taxed * 2 }} 円</p>',
})
export class PriceTagComponent {
  price = signal(1000);
}

2深いプロパティアクセスを短くする

user()?.profile?.address?.city のような深いアクセスは、繰り返し書くとテンプレートが読みにくくなります。@let で一度だけ書いて名前を付ければ、以降は短い変数名で済み、?. の連鎖も1か所にまとまります。

import { Component, signal } from '@angular/core';

type User = { profile?: { address?: { city: string } } };

@Component({
  selector: 'app-shipping-info',
  // 深いアクセスを1か所にまとめ、以降は city で参照する
  template:
    '@let city = user()?.profile?.address?.city ?? "未設定";' +
    '<p>お届け先: {{ city }}</p>' +
    '<p>{{ city }}への配送は3日かかります</p>',
})
export class ShippingInfoComponent {
  user = signal<User | null>({ profile: { address: { city: '札幌' } } });
}

3@for の行ごとに小計を作る

@let は @for の中でも使え、その行だけで有効なローカル変数になります。行ごとの小計のような「各要素から導く値」に名前を付けると、繰り返し部分のテンプレートがすっきりします。

import { Component, signal } from '@angular/core';

type Item = { id: number; name: string; price: number; qty: number };

@Component({
  selector: 'app-cart-list',
  // 行ごとに subtotal を宣言する。スコープはその繰り返しの中だけ
  template:
    '<ul>' +
    '  @for (item of items(); track item.id) {' +
    '    @let subtotal = item.price * item.qty;' +
    '    <li>{{ item.name }}: {{ subtotal }} 円</li>' +
    '  }' +
    '</ul>',
})
export class CartListComponent {
  items = signal<Item[]>([
    { id: 1, name: 'りんご', price: 150, qty: 3 },
    { id: 2, name: 'メロン', price: 1200, qty: 1 },
  ]);
}

4長い条件式に名前を付けて @if で使う

複数の条件を && でつないだ式は、@let で canSubmit のような意図が伝わる名前を付けてから @if や [disabled] に渡すと読みやすくなります。条件の意味が名前で説明されるので、テンプレートがそのまま仕様書のように読めます。

import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-submit-area',
  // 条件のかたまりに「送信できるか」という名前を付ける
  template:
    '@let canSubmit = name().length > 0 && agreed() && !sending();' +
    '<button [disabled]="!canSubmit">送信</button>' +
    '@if (!canSubmit) {' +
    '  <p>入力と同意を済ませてください</p>' +
    '}',
})
export class SubmitAreaComponent {
  name = signal('');
  agreed = signal(false);
  sending = signal(false);
}

5@let のスコープは宣言したブロックの中だけ

@if ブロックの中で宣言した @let は、そのブロックの外からは参照できません。外でも使いたい変数はテンプレートの先頭側(ブロックの外)で宣言する、というスコープの感覚はJavaScriptの let と同じです。

import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-scope-demo',
  // total は外で宣言したのでどこでも使える。
  // discounted は @if の中で宣言したので中でしか使えない
  template:
    '@let total = price() * qty();' +
    '<p>合計 {{ total }} 円</p>' +
    '@if (total >= 5000) {' +
    '  @let discounted = total * 0.9;' +
    '  <p>1割引で {{ discounted }} 円!</p>' +
    '}' +
    '<!-- ここで {{ discounted }} と書くとコンパイルエラー -->',
})
export class ScopeDemoComponent {
  price = signal(1200);
  qty = signal(5);
}