Pipe(パイプ)
| 対応: | Angular 14(2022) |
|---|
『Angular』のパイプは、テンプレート内でデータを表示用に変換するフィルター機能です。|(パイプ)記号を使って値に適用し、日付・数値・通貨・大文字小文字変換など、よく使う変換処理を簡潔に記述できます。Angular には標準で多数のビルトインパイプが用意されており、独自のカスタムパイプを作成することも可能です。
構文
<!-- 基本構文: 値 | パイプ名 -->
<p>{{ value | pipeName }}</p>
<!-- 引数を渡す場合: 値 | パイプ名:引数1:引数2 -->
<p>{{ value | pipeName:arg1:arg2 }}</p>
<!-- 複数のパイプを連結する場合(左から順に適用されます) -->
<p>{{ value | pipe1 | pipe2 }}</p>
ビルトインパイプ一覧
| パイプ名 | 用途 | 例 |
|---|---|---|
date | 日付を指定フォーマットに変換します。 | {{ today | date:'yyyy/MM/dd' }} |
number | 数値を桁区切りや小数点以下の桁数を指定して表示します。 | {{ price | number:'1.0-0' }} |
currency | 数値を通貨形式に変換します。 | {{ price | currency:'JPY':'symbol':'1.0-0' }} |
percent | 数値をパーセント表示に変換します。 | {{ ratio | percent:'1.1-1' }} |
uppercase | 文字列を大文字に変換します。 | {{ 'hello' | uppercase }} |
lowercase | 文字列を小文字に変換します。 | {{ 'HELLO' | lowercase }} |
titlecase | 単語の先頭を大文字に変換します。 | {{ 'hello world' | titlecase }} |
json | オブジェクトを JSON 文字列として表示します(デバッグ用途に便利です)。 | {{ obj | json }} |
slice | 配列や文字列の一部を切り出して返します。 | {{ items | slice:0:3 }} |
keyvalue | オブジェクトをキーと値のペアの配列に変換します。 | {{ map | keyvalue }} |
async | Observable や Promise の値を自動で購読・解除して表示します。 | {{ data$ | async }} |
i18nSelect | キーに対応した文字列マッピングから値を選択して表示します。 | {{ gender | i18nSelect:genderMap }} |
i18nPlural | 数値に応じた複数形の文字列を表示します。 | {{ count | i18nPlural:itemPluralMap }} |
サンプルコード
ビルトインパイプを使ってさまざまなデータ型を表示する基本的な例です。
// product-detail.component.ts
// 商品の詳細情報をビルトインパイプで整形して表示するコンポーネントです
import { Component } from '@angular/core';
import { CommonModule, DatePipe, CurrencyPipe, UpperCasePipe } from '@angular/common';
@Component({
selector: 'app-product-detail',
templateUrl: './product-detail.component.html',
imports: [CommonModule],
standalone: true,
})
export class ProductDetailComponent {
// 商品名(大文字変換のデモ用)
productName: string = 'angular starter kit';
// 販売価格(通貨フォーマットのデモ用)
price: number = 12800;
// 在庫数(数値フォーマットのデモ用)
stock: number = 1234567;
// 割引率(パーセント表示のデモ用)
discountRate: number = 0.15;
// 発売日(日付フォーマットのデモ用)
releaseDate: Date = new Date(2025, 3, 1);
}
<!-- product-detail.component.html -->
<!-- ビルトインパイプを使って各プロパティを見やすい形式で表示します -->
<!-- titlecase で各単語の先頭を大文字に変換します -->
<h1>{{ productName | titlecase }}</h1>
<!-- currency で日本円表示・小数点なし・3桁区切りにします -->
<p>価格: {{ price | currency:'JPY':'symbol':'1.0-0' }}</p>
<!-- number で3桁区切りの整数として表示します -->
<p>在庫数: {{ stock | number:'1.0-0' }} 個</p>
<!-- percent で小数点以下1桁のパーセント表示にします -->
<p>割引率: {{ discountRate | percent:'1.1-1' }}</p>
<!-- date で 'yyyy年MM月dd日' 形式にフォーマットします -->
<p>発売日: {{ releaseDate | date:'yyyy年MM月dd日' }}</p>
カスタムパイプを作成して独自の変換ロジックを実装する例です。
// truncate.pipe.ts
// 長い文字列を指定した文字数で切り詰めて末尾に「...」を付けるカスタムパイプです
import { Pipe, PipeTransform } from '@angular/core';
// @Pipe デコレーターでパイプ名を指定します(テンプレートで | truncate と書いて使います)
@Pipe({
name: 'truncate',
standalone: true,
// pure: true がデフォルトです。入力値が変わったときだけ再計算されます
})
export class TruncatePipe implements PipeTransform {
// transform メソッドに変換ロジックを記述します
// value: パイプに渡される元の値
// limit: 切り詰める文字数(デフォルト50文字)
// trail: 末尾に付ける文字列(デフォルト「...」)
transform(value: string, limit: number = 50, trail: string = '...'): string {
// 値が空、または制限文字数以内であればそのまま返します
if (!value || value.length <= limit) {
return value;
}
// 指定文字数で切り詰めて末尾文字列を付けて返します
return value.substring(0, limit) + trail;
}
}
// article-list.component.ts
// 記事一覧を表示するコンポーネントです。TruncatePipe を imports して使用します
import { Component } from '@angular/core';
import { TruncatePipe } from './truncate.pipe';
// 記事データの型定義です
interface Article {
id: number;
title: string;
body: string;
}
@Component({
selector: 'app-article-list',
template: `
<ul>
<!-- body を truncate パイプで100文字に切り詰めて表示します -->
<li *ngFor="let article of articles">
<strong>{{ article.title }}</strong>
<p>{{ article.body | truncate:100 }}</p>
</li>
</ul>
`,
imports: [TruncatePipe],
standalone: true,
})
export class ArticleListComponent {
articles: Article[] = [
{
id: 1,
title: 'Angular パイプ入門',
body: 'パイプはテンプレート内でデータを変換するための仕組みです。ビルトインパイプを活用することで日付・通貨・文字列などを簡単に整形できます。カスタムパイプを作成すれば独自の変換ロジックも再利用可能な形で管理できます。',
},
{
id: 2,
title: 'RxJS と async パイプ',
body: 'async パイプを使うと Observable や Promise の値を自動で購読し、コンポーネントが破棄されたときに自動でサブスクリプションを解除してくれます。メモリリーク対策として活用されることが多いです。',
},
];
}
async パイプを使って Observable を安全に購読する例です。
// user-profile.component.ts
// async パイプで Observable を購読し、コンポーネント破棄時に自動解除する例です
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Observable } from 'rxjs';
import { UserService } from './user.service';
// ユーザーデータの型定義です
interface User {
name: string;
email: string;
}
@Component({
selector: 'app-user-profile',
template: `
<!-- async パイプが自動で購読・解除を管理します -->
<ng-container *ngIf="user$ | async as user">
<p>名前: {{ user.name }}</p>
<p>メール: {{ user.email | lowercase }}</p>
</ng-container>
`,
imports: [CommonModule],
standalone: true,
})
export class UserProfileComponent implements OnInit {
// Observable をプロパティに持ち、テンプレートで async パイプを通して表示します
user$!: Observable<User>;
constructor(private userService: UserService) {}
ngOnInit(): void {
// subscribe せずに Observable をそのままプロパティに代入します
// 購読・解除は async パイプが自動で行うため手動の unsubscribe は不要です
this.user$ = this.userService.getUser();
}
}
pure パイプと impure パイプ
| 種別 | 再計算のタイミング | 概要 |
|---|---|---|
| pure パイプ(デフォルト) | 入力値の参照または値が変わったとき | Angular の変更検知が効率的に動作します。配列やオブジェクトを直接変更(ミューテーション)しても再計算されないため、新しい参照を渡す必要があります。 |
| impure パイプ | 変更検知が走るたび(毎回) | @Pipe({ pure: false }) と指定します。配列の中身が変わったときにも再計算されますが、頻繁に呼ばれるためパフォーマンスに注意が必要です。 |
注意点
| 項目 | 説明 |
|---|---|
| パイプでの副作用禁止 | transform メソッド内でサービスへのデータ保存や外部 API 呼び出しなどの副作用を持たせないようにしてください。パイプは純粋な変換処理のみを担当するのが原則です。 |
| pure パイプと配列・オブジェクト | pure パイプは参照の変化でのみ再計算されます。既存配列に push してもビューが更新されない場合は、スプレッド構文などで新しい配列参照を作成して渡してください。 |
| standalone でのインポート | standalone コンポーネントでビルトインパイプ(DatePipe など)を単体で使う場合、CommonModule 全体ではなく必要なパイプのクラスだけを imports に指定するとバンドルサイズを削減できます。 |
| locale 設定 | date・currency・number パイプは locale に依存します。日本語ロケールを適用するには app.config.ts で provideAppInitializer または LOCALE_ID トークンで 'ja' を提供してください。 |
| パイプのテスト | パイプは transform メソッドを持つクラスなので、Angular のテストユーティリティを使わなくても new TruncatePipe().transform('...') のように直接インスタンス化して単体テストできます。 |
概要
パイプは『Angular』のテンプレートでデータを表示用に変換する宣言的な仕組みです。| 記号を使って直感的に記述でき、日付・数値・通貨・文字列変換などよく使う処理はビルトインパイプとして標準提供されています。独自の変換処理は @Pipe デコレーターと PipeTransform インターフェースを使ってカスタムパイプとして定義できます。
pure パイプはデフォルトの動作で変更検知のパフォーマンスが最適化されています。配列やオブジェクトを扱う場合は参照の変化に注意し、必要に応じて新しい参照を渡すように設計してください。また async パイプは Observable の購読・解除を自動で行うため、ngOnDestroy での手動 unsubscribe を省略できます。
パイプに @Pipe デコレーターを付ける詳細な書き方については Pipe デコレーター をご覧ください。テンプレート構文全体の概要については テンプレート構文 も参照してください。
よくあるミス: pure パイプで配列を直接変更しても表示が更新されない
pure パイプ(デフォルト)は入力値の参照が変わったときだけ再計算されます。配列に push() で要素を追加しても参照は同じオブジェクトのままなので、パイプは再実行されず表示が更新されません。新しい配列参照を渡すことで更新されます。
NG
// fighters.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-fighters',
template: `
<ul>
<li *ngFor="let f of fighters | slice:0:3">{{ f }}</li>
</ul>
<button (click)="addFighter()">追加</button>
`,
imports: [CommonModule],
standalone: true,
})
export class FightersComponent {
fighters: string[] = ['Son Goku', 'Vegeta', 'Piccolo'];
addFighter(): void {
// push() は同じ配列参照を変更するため、pure パイプは再実行されません
this.fighters.push('Krillin');
}
}
OK
// fighters.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-fighters',
template: `
<ul>
<li *ngFor="let f of fighters | slice:0:3">{{ f }}</li>
</ul>
<button (click)="addFighter()">追加</button>
`,
imports: [CommonModule],
standalone: true,
})
export class FightersComponent {
fighters: string[] = ['Son Goku', 'Vegeta', 'Piccolo'];
addFighter(): void {
// スプレッド構文で新しい配列参照を作ると pure パイプが再実行されます
this.fighters = [...this.fighters, 'Krillin'];
}
}
よくあるミス: カスタムパイプを imports に追加し忘れる
standalone コンポーネントでカスタムパイプを使う場合、@Component デコレーターの imports 配列にそのパイプクラスを追加する必要があります。追加し忘れると「Unknown pipe」エラーになります。
NG
// profile.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-profile',
template: `<p>{{ name | truncate:10 }}</p>`,
// TruncatePipe を imports に追加していないためエラーになります
standalone: true,
})
export class ProfileComponent {
name: string = 'Son Goku';
}
OK
// profile.component.ts
import { Component } from '@angular/core';
import { TruncatePipe } from './truncate.pipe';
@Component({
selector: 'app-profile',
template: `<p>{{ name | truncate:10 }}</p>`,
// カスタムパイプは必ず imports に追加します
imports: [TruncatePipe],
standalone: true,
})
export class ProfileComponent {
name: string = 'Son Goku';
}
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。