詳細ガイド
テンプレート

パイプ

概要

パイプは、Angularテンプレート式で、テンプレート内のデータを宣言的に変換することを可能にする特別な演算子です。パイプを使用すると、変換関数を一度宣言し、その変換を複数のテンプレートで使用できます。Angularのパイプは、Unixパイプに着想を得て、縦棒文字(|)を使用します。

NOTE: Angularのパイプ構文は、縦棒文字をビット単位のOR演算子に使用する標準的なJavaScriptとは異なります。Angularテンプレート式では、ビット単位の演算子はサポートされていません。

Angularが提供するいくつかの組み込みパイプを使用した例を以下に示します。

import {Component} from '@angular/core';
import {CurrencyPipe, DatePipe, TitleCasePipe} from '@angular/common';

@Component({
  selector: 'app-root',
  imports: [CurrencyPipe, DatePipe, TitleCasePipe],
  template: `
    <main>
      <!-- Transform the company name to title-case and
       transform the purchasedOn date to a locale-formatted string -->
      <h1>Purchases from {{ company | titlecase }} on {{ purchasedOn | date }}</h1>

      <!-- Transform the amount to a currency-formatted string -->
      <p>Total: {{ amount | currency }}</p>
    </main>
  `,
})
export class ShoppingCart {
  amount = 123.45;
  company = 'acme corporation';
  purchasedOn = '2024-07-08';
}

Angularがコンポーネントをレンダリングすると、ユーザーのロケールに基づいて適切な日付形式と通貨が保証されます。ユーザーが米国にいる場合、次のようにレンダリングされます。

<main>
  <h1>Purchases from Acme Corporation on Jul 8, 2024</h1>
  <p>Total: $123.45</p>
</main>

Angularによる値のローカリゼーションの詳細については、i18nに関する詳細ガイドを参照してください。

組み込みパイプ

Angularには、@angular/commonパッケージに組み込みパイプのセットが含まれています。

名前 説明
AsyncPipe PromiseまたはRxJSObservableから値を読み取ります。
CurrencyPipe ロケールルールに従って、数値を通貨文字列に変換します。
DatePipe ロケールルールに従ってDate値をフォーマットします。
DecimalPipe ロケールルールに従って、数値を小数点を含む文字列に変換します。
I18nPluralPipe ロケールルールに従って、値を値の複数形にする文字列にマップします。
I18nSelectPipe キーを、目的の値を返すカスタムセレクターにマップします。
JsonPipe デバッグのために、オブジェクトをJSON.stringifyを使用した文字列表現に変換します。
KeyValuePipe オブジェクトまたはMapを、キーと値のペアの配列に変換します。
LowerCasePipe テキストをすべて小文字に変換します。
PercentPipe ロケールルールに従って、数値をパーセンテージ文字列に変換します。
SlicePipe 要素のサブセット(スライス)を含む新しい配列または文字列を作成します。
TitleCasePipe テキストをタイトルケースに変換します。
UpperCasePipe テキストをすべて大文字に変換します。

パイプの使用

Angularのパイプ演算子は、テンプレート式内で縦棒文字(|)を使用します。パイプ演算子は二項演算子です。左側のオペランドは変換関数に渡される値であり、右側のオペランドはパイプの名前とその後の追加の引数(下記参照)です。

<p>Total: {{ amount | currency }}</p>

この例では、amountの値は、パイプ名がcurrencyであるCurrencyPipeに渡されます。その後、ユーザーのロケールに対するデフォルトの通貨がレンダリングされます。

同じ式で複数のパイプを組み合わせる

複数のパイプ演算子を使用することで、値に複数の変換を適用できます。Angularはパイプを左から右に実行します。

次の例は、ローカライズされた日付をすべて大文字で表示するために、パイプの組み合わせを示しています。

<p>The event will occur on {{ scheduledOn | date | uppercase }}.</p>

パイプにパラメータを渡す

一部のパイプは、変換を構成するためのパラメータを受け入れます。パラメータを指定するには、パイプ名の後にコロン(:)とパラメータ値を付けます。

たとえば、DatePipeは、日付を特定の方法でフォーマットするためのパラメータを受け取ることができます。

<p>The event will occur at {{ scheduledOn | date: 'hh:mm' }}.</p>

一部のパイプは、複数のパラメータを受け入れる場合があります。追加のパラメータ値は、コロン文字(:)で区切って指定できます。

たとえば、タイムゾーンを制御するために、2番目のオプションのパラメータを渡すこともできます。

<p>The event will occur at {{ scheduledOn | date: 'hh:mm' : 'UTC' }}.</p>

パイプの動作

概念的には、パイプは入力値を受け取り、変換された値を返す関数です。

import {Component} from '@angular/core';
import {CurrencyPipe} from '@angular/common';

@Component({
  selector: 'app-root',
  imports: [CurrencyPipe],
  template: `
    <main>
      <p>Total: {{ amount | currency }}</p>
    </main>
  `,
})
export class AppComponent {
  amount = 123.45;
}

この例では、次のようになります。

  1. CurrencyPipe@angular/commonからインポートされます
  2. CurrencyPipeimports配列に追加されます
  3. amountデータはcurrencyパイプに渡されます

パイプ演算子の優先順位

パイプ演算子は、+, -, *, /, %, &&, ||, ??などの他の二項演算子よりも優先順位が低くなっています。

<!-- firstName and lastName are concatenated before the result is passed to the uppercase pipe -->
{{ firstName + lastName | uppercase }}

パイプ演算子は、条件(三項)演算子よりも優先順位が高くなっています。

{{ (isAdmin ? 'Access granted' : 'Access denied') | uppercase }}

同じ式を括弧なしで記述した場合:

{{ isAdmin ? 'Access granted' : 'Access denied' | uppercase }}

代わりに次のように解析されます。

{{ isAdmin ? 'Access granted' : ('Access denied' | uppercase) }}

演算子の優先順位が不明確な場合は、常に式に括弧を使用してください。

パイプによる変更検知

デフォルトでは、すべてのパイプはpureとみなされます。これは、プリミティブな入力値(StringNumberBooleanSymbolなど)またはオブジェクト参照(ArrayObjectFunctionDateなど)が変更されたときにのみ実行されることを意味します。Pureなパイプは、渡された値が変更されていない場合にAngularが変換関数の呼び出しを回避できるため、パフォーマンス上の利点があります。

その結果、オブジェクトプロパティや配列項目の変更は、オブジェクトまたは配列参照の全体が別のインスタンスに置き換えられない限り検出されません。このレベルの変更検知が必要な場合は、配列やオブジェクト内の変更の検出を参照してください。

カスタムパイプの作成

@Pipeデコレーターを使用してTypeScriptクラスを実装することで、カスタムパイプを定義できます。パイプには、次の2つの要素が必要です。

  • パイプデコレーターで指定された名前
  • 値を変換するtransformという名前のメソッド

TypeScriptクラスは、さらにPipeTransformインターフェースを実装して、パイプの型シグネチャを満たす必要があります。

文字列をケバブケースに変換するカスタムパイプの例を以下に示します。

// kebab-case.pipe.ts
import {Pipe, PipeTransform} from '@angular/core';

@Pipe({
  name: 'kebabCase',
})
export class KebabCasePipe implements PipeTransform {
  transform(value: string): string {
    return value.toLowerCase().replace(/ /g, '-');
  }
}

@Pipeデコレーターの使用

カスタムパイプを作成する際は、@angular/coreパッケージからPipeをインポートし、TypeScriptクラスのデコレーターとして使用します。

import {Pipe} from '@angular/core';

@Pipe({
  name: 'myCustomTransformation',
})
export class MyCustomTransformationPipe {}

@Pipe デコレーターは name を必要とします。この name は、テンプレート内でパイプをどのように使用するかを制御します。

カスタムパイプの名前付け規則

カスタムパイプの名前付け規則は、次の2つの規則で構成されます。

  • name - camelCaseが推奨されます。ハイフンは使用しないでください。
  • クラス名 - nameのPascalCaseバージョンにPipeを追加したもの

PipeTransformインターフェースの実装

@Pipeデコレーターに加えて、カスタムパイプは常に@angular/corePipeTransformインターフェースを実装する必要があります。

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({
  name: 'myCustomTransformation',
})
export class MyCustomTransformationPipe implements PipeTransform {}

このインターフェースを実装することで、パイプクラスが正しい構造を持つことが保証されます。

パイプの値を変換する

すべての変換は、最初の引数が渡される値で、戻り値が変換された値であるtransformメソッドによって呼び出されます。

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({
  name: 'myCustomTransformation',
})
export class MyCustomTransformationPipe implements PipeTransform {
  transform(value: string): string {
    return `My custom transformation of ${value}.`;
  }
}

カスタムパイプにパラメータを追加する

transformメソッドにadditional parametersを追加することで、変換にパラメータを追加できます。

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({
  name: 'myCustomTransformation',
})
export class MyCustomTransformationPipe implements PipeTransform {
  transform(value: string, format: string): string {
    let msg = `My custom transformation of ${value}.`;

    if (format === 'uppercase') {
      return msg.toUpperCase();
    } else {
      return msg;
    }
  }
}

配列またはオブジェクト内の変更を検出する

パイプで配列またはオブジェクト内の変更を検出する必要がある場合は、pureフラグをfalseの値で渡すことで、パイプを不純な関数としてマークする必要があります。

IMPORTANT: 不純なパイプは、本当に必要な場合にのみ作成してください。不純なパイプは、注意深く使用しないと、パフォーマンスに大きな影響を与える可能性があります。

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({
  name: 'joinNamesImpure',
  pure: false,
})
export class JoinNamesImpurePipe implements PipeTransform {
  transform(names: string[]): string {
    return names.join();
  }
}

Angular開発者は、パイプのnameとクラス名にImpureを含めるという慣習を採用して、他の開発者に潜在的なパフォーマンス上の問題を知らせます。

Using pipe logic outside templates

Pipes are template operators designed to transform data declaratively in templates. When you need the same transformation logic in a service, a utility class, or any other non-template context, avoid injecting the pipe class. Pipes are not designed as injectable services.

Extract the logic from custom pipes

When you create a custom pipe, extract the transformation into a standalone function. Have the pipe's transform() method delegate to that function, and import the function directly wherever else you need it.

kebab-case.ts

export function toKebabCase(value: string): string {
  return value.toLowerCase().replace(/ /g, '-');
}
Prefer

kebab-case.pipe.ts

import {Pipe, PipeTransform} from '@angular/core';
import {toKebabCase} from './kebab-case';

@Pipe({name: 'kebabCase'})
export class KebabCasePipe implements PipeTransform {
  transform(value: string): string {
    return toKebabCase(value);
  }
}
Prefer

formatter.service.ts

import {Service} from '@angular/core';
import {toKebabCase} from './kebab-case';

@Service()
export class FormatterService {
  formatSlug(title: string): string {
    return toKebabCase(title);
  }
}
Avoid

formatter.service.ts

import {Service} from '@angular/core';
import {KebabCasePipe} from './kebab-case.pipe';

@Service()
export class FormatterService {
  // Avoid injecting the pipe class into services or other classes.
  private kebabCasePipe = inject(KebabCasePipe);

  formatSlug(title: string): string {
    return this.kebabCasePipe.transform(title);
  }
}

Use formatting functions for built-in pipes

Angular's built-in locale-aware pipes each have a corresponding standalone formatting function exported from @angular/common. Use these functions instead of injecting the pipe class.

Prefer
import {Service, LOCALE_ID, inject} from '@angular/core';
import {formatNumber} from '@angular/common';

@Service()
export class PriceService {
  private locale = inject(LOCALE_ID);

  format(value: number) {
    return formatNumber(value, this.locale, '1.2-2');
  }
}
Avoid
import {Service} from '@angular/core';
import {DecimalPipe} from '@angular/common';

@Service()
export class PriceService {
  private decimalPipe = inject(DecimalPipe);

  format(value: number) {
    return this.decimalPipe.transform(value, '1.2-2');
  }
}