Dev Study
← 解説「input() — 親から値を受け取る」に戻る

サンプルコードで身につける: input() — 親から値を受け取る

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

1親から文字列を1つ受け取る

input.required の最小例で、親が [userName] で渡した文字列を子が表示します。受け取った値はシグナルなので userName() と関数として読む、という点が最初のつまずきポイントです。「親がデータを持ち、子は受け取って表示する」関係の出発点になります。

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

@Component({
  selector: 'app-user-name',
  // 受け取った値はシグナルなので userName() と読む
  template: '<p>{{ userName() }}さん</p>',
})
export class UserNameComponent {
  // 親から必ず渡してもらう入力
  userName = input.required<string>();
}

// 親側: <app-user-name [userName]="loginUser" />

2デフォルト値付きの省略可能な入力

input('概要') のように初期値を渡すと、親が値を渡さなかったときにその値が使われます。「必須の入力は required、任意の入力はデフォルト値」という使い分けの基本形です。見出しコンポーネントのように、細かい調整は任意で受け付ける部品でよく使います。

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

@Component({
  selector: 'app-page-title',
  template: '<h1>{{ titleText() }}</h1><p>{{ subtitle() }}</p>',
})
export class PageTitleComponent {
  titleText = input.required<string>();
  subtitle = input('概要'); // 渡されなければ '概要' になる
}

// 親側: <app-page-title [titleText]="pageName" />
// subtitle を渡さなければ「概要」と表示される

3オブジェクトを型付きで受け取る

input は文字列だけでなく、型引数を指定すればオブジェクトでも数値でも受け取れます。商品オブジェクトを丸ごと受け取る形は、関連する値をバラバラに渡すより宣言が少なく済み、型チェックも効きます。一覧の1行・1カードを表す子コンポーネントの定番スタイルです。

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

type Product = { id: number; productName: string; price: number };

@Component({
  selector: 'app-price-tag',
  // オブジェクトのプロパティには product().price のようにアクセスする
  template: '<p>{{ product().productName }}: {{ product().price }} 円</p>',
})
export class PriceTagComponent {
  product = input.required<Product>();
}

// 親側: <app-price-tag [product]="selectedProduct" />

4受け取った値で表示を出し分ける

input で受け取った在庫数をそのまま @if の条件に使い、子の中で表示を出し分ける例です。親は数値を渡すだけでよく、「どう見せるか」の判断は子に閉じ込められています。バッジやステータス表示など、判断ロジック付きの小さな表示部品はこの形で作ります。

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

@Component({
  selector: 'app-stock-badge',
  // 受け取ったシグナルはテンプレートの条件式にそのまま使える
  template:
    '@if (stock() === 0) { <span>在庫切れ</span> }' +
    ' @else if (stock() <= 3) { <span>残りわずか</span> }' +
    ' @else { <span>在庫あり</span> }',
})
export class StockBadgeComponent {
  stock = input.required<number>();
}

// 親側: <app-stock-badge [stock]="item.stock" />

5一覧の親から各カードへデータを配る

親が記事の配列を持ち、@for で繰り返しながら各記事を子カードへ input で渡す、実務の一覧画面そのままの構成です。「一覧の管理は親、1件の見せ方は子」という分業によって、それぞれのコンポーネントが小さく保たれます。input が親子分業の配線役であることがよく分かる例です。

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

type Article = { id: number; articleTitle: string };

@Component({
  selector: 'app-article-card',
  template: '<article><h2>{{ article().articleTitle }}</h2></article>',
})
export class ArticleCardComponent {
  article = input.required<Article>();
}

@Component({
  selector: 'app-article-list',
  imports: [ArticleCardComponent],
  // 繰り返しの中で、1件ずつ子へ渡す
  template:
    '@for (a of articles; track a.id) { <app-article-card [article]="a" /> }',
})
export class ArticleListComponent {
  articles: Article[] = [
    { id: 1, articleTitle: 'シグナル入門' },
    { id: 2, articleTitle: 'DIの基礎' },
  ];
}