組み込みPipesで対応できない変換をカスタムPipesで実現
組み込みPipesで対応できない変換は、カスタムPipesを実装して実現できます。図6のような数値を2進数/8進数/16進数に変換して表示するサンプルで、カスタムPipesの実装方法を説明します。
このカスタムPipesの実装はリスト7のようになります。
// カスタムPipesの記述に必要な変数を参照 ...(1)
import { Pipe, PipeTransform } from '@angular/core';
// Pipes宣言と名前の指定 ...(2)
@Pipe({name: "base"})
export class NumberBasePipe implements PipeTransform {
// PipeTransformインタフェースのtransformメソッド ...(3)
transform(valueString: string, base:number): string {
console.log("NumberBasePipe value=" + valueString + ", base=" + base);
// 入力文字列を数値に変換(変換できないときは入力文字列を返却) ...(4)
let value = Number.parseInt(valueString);
if (isNaN(value)) { return valueString; }
// 2進数の場合 ...(5)
if (base === 2) {
return "0b" + value.toString(base);
}
// 8進数の場合 ...(6)
else if (base === 8) {
return "0o" + value.toString(base);
}
// 16進数の場合 ...(7)
else if (base === 16) {
return "0x" + value.toString(base);
}
// それ以外の場合は10進数として扱う ...(8)
else {
return value.toString();
}
}
}
(1)で、カスタムPipesに必要なPipeデコレーターとPipeTransformインタフェースを参照しています。(2)のようにPipeデコレーターに名前(base)を指定して、PipeのクラスNumberBasePipeを定義しています。
Pipesの変換処理は、PipeTransformインタフェースで定義されるtransformメソッド(3)に実装します。transformメソッドの第1引数には変換前の変数、第2引数以降は「:」で区切って指定するパラメーターが渡されるので、これらから変換後の文字列を作成して返却するよう実装します。まず、変換前の変数valueStringを(4)で数値に変換します。数値に変換できないときはvalueStringの値をそのまま返却して処理を終了し、変換できた場合は基数パラメーターbaseの値に応じて(5)~(7)で2進数/8進数/16進数に変換して返却します。基数が2、8、16のいずれでもない場合は、(8)で10進数をそのまま返却します。
リスト7のカスタムPipesを利用するには、モジュール定義ファイルapp.modules.tsにリスト8の(1)(2)のように記述して、カスタムPipesを設定します。
// カスタムPipesの参照 ...(1)
import { NumberBasePipe } from "./numberbase.pipe";
@NgModule({
(略)
declarations: [
AppComponent,
NumberBasePipe // カスタムPipesを設定 ...(2)
],
(略)
})
カスタムPipesはコンポーネントでリスト9のように利用できます。「base:」の後のパラメーターに、2進数/8進数/16進数のどれに変換するかを数字で指定します。
<div>2進数:{{inputValue | base:2}}</div>
<div>8進数:{{inputValue | base:8}}</div>
<div>16進数:{{inputValue | base:16}}</div>
Angularの変更監視処理とPure Pipes/Impure Pipesの関係
ここまで説明してきたPipesは、Angularの変更監視処理で、文字や数値の変更やオブジェクトが置換されたことを検出したときのみ実行されます。このようなPipesを「Pure Pipes」と呼びます。
Angularは、オブジェクトの内容変更(Date型オブジェクトの年だけを変更する場合や、配列への要素追加など)を無視するため、このときPure Pipesは実行されません。オブジェクトの内容変更時にPipesを実行させる場合は、PureでないPipes(「Impure pipes」)にする必要があります。例えば、リスト7のカスタムPipesをImpure pipesにするには、リスト10のように@Pipeデコレーターのpure変数にfalseを指定します。
// Pipes宣言と名前の指定
@Pipe({
name: "base",
pure: false
})
Impure pipesは、Angularの変更監視処理の都度、変更を検出したかどうかにかかわらず実行されます。リスト10を実行してデバッグログを表示させると、ページロード時やテキストボックスのフォーカス変更時などに、頻繁にPipesが実行されます。
一方、Pure pipes(リスト7)はページロード時の初回に1回だけ実行され、テキストボックスのフォーカスを変更しても、値が変更されなければ実行されません。
Impure pipesは実行頻度が高いため、カスタムPipesは可能な限りPure pipesとするのが望ましいです。どうしてもImpureにするときは、Pipes内で重たい処理が繰り返し実行されないように注意して実装する必要があります。
[補足]AngularJS 1のfilterとorderbyに対応するPipesが存在しないわけ
Angularには、配列のインデックスで範囲を指定するPipes(SlicePipe)が存在しますが、フィルターや並び替えを行うPipesは存在しません。AngularJS 1にはfilterやorderbyとして存在していましたが、Angularでは以下の理由から意図的に実装されていません。
- フィルターや並び替えの処理はオブジェクト配列が対象となるため、上述のImpure Pipesにする必要があり、実行速度の低下が懸念されること。
- フィルターや並び替えの条件として指定するプロパティ名が、JavaScriptの最小化で別の名前に変更されて問題を起こす可能性があること。
フィルターや並び替えの処理は、Pipesではなくコンポーネントに直接実装するよう、Angularの公式ドキュメントで案内されています。
まとめ
本記事では、変数を表示用に変換するAngularのPipesについて説明しました。Pipesを利用すると、画面表示のための変換処理をコンポーネントの処理から分離して、柔軟な画面表示を実現できます。利用頻度が高い変換はAngularが標準で用意しているほか、カスタムPipesでより細かい変換処理を作り込むこともできます。
