Pipesの国際化サポート
Pipesは、変数を画面表示に適した内容に変換する機能です。Angular 4まで、数値や日時、通貨のPipesは、ブラウザーの国際化API(ECMAScript Internationalization API)を内部的に利用しており、APIに非対応のブラウザーで動作させるためにはPolyfill(互換ライブラリー)が必要でした。それに対してAngular 5では、Pipesの国際化処理をブラウザーのAPIに頼らず、独自で行うようになりました。実行するコマンドで「ng serve --aot --locale=ja-JP」のように、「--locale」オプションでロケールを指定できます。
コマンドオプションを指定せずに日本語ロケールを利用するには、モジュール定義ファイルでリスト3のように記述します。(1)で日本語ロケールデータをlocaleJa変数にインポートして、それをregisterLocaleDataメソッドで登録します。(2)のLOCALE_IDで、登録した日本語ロケールでアプリが動作するように指定します。
// 日本語ロケールを登録 ...(1) import { registerLocaleData } from '@angular/common' import localeJa from '@angular/common/locales/ja'; registerLocaleData(localeJa); @NgModule({ (略) providers: [ {provide:LOCALE_ID, useValue:'ja-JP'} // 日本語ロケールを設定 ...(2) ], (略) })
リスト3の設定を適用してPipesを実行すると、図2の通り表示されます。
図2で表示したPipesの内容をリスト4に示します。sampleValueには日時(JavaScriptのDate型変数)が設定されます。図2とリスト4、特にフォーマットに「fullDate」を指定した(1)の部分を見比べると、表示が日本語対応していることが分かります。
<div>デフォルト:{{sampleValue | date}}</div> <div>fullDate:{{sampleValue | date:"fullDate"}}</div><!--(1)--> <div>shortTime:{{sampleValue | date:"shortTime"}}</div> <div>yyyyMMss_HHmmss:{{sampleValue | date:"yyyyMMss_HHmmss"}}</div>
Pipesの国際化をAngular 4以前と同じ挙動にするには、リスト5(1)のように、モジュール定義ファイルでDeprecatedI18NPipesModuleをインポートします。
@NgModule({ (略) imports: [ BrowserModule, DeprecatedI18NPipesModule // Pipesが従来の挙動になるモジュール ...(1) ], providers: [ {provide:LOCALE_ID, useValue:'ja-JP'} // 日本語ロケールを設定 ], (略) })
リスト5の設定でリスト4のPipeを実行すると、図3の通り表示されます。Angular 4以前の挙動になるため、「デフォルト」と「fullDate」の表示が図2と異なるほか、Microsoft Edgeの国際化APIの挙動によって、「yyyyMMdd_HHmmss」とフォーマットを指定した表示が不正になっています(過去記事も参考にしてください)。
なお、国際化サポートの変更に伴い、フォーマット文字列の指定方法が細かく変更になっています。変更の内容をまとめた一覧表が公開されています。
RxJSとHTTP機能の変更点
AngularにReactive Extensionの機能を提供するRxJSが5.5にバージョンアップされて、インポートの記述形式がより分かりやすくなりました。また、Angular 4.3で導入された新しいHTTP機能HttpClientがAngular 5で推奨となり、従来のHTTP機能(@angular/http)は非推奨になりました。
これらの変更について、RxJSとHTTP機能の新旧形式を組み合わせて実装した、表1のサンプルコードで説明していきます。
サンプルコード名 | RxJSの記法 | HTTP機能の記法 |
---|---|---|
P003-http1 | 従来の形式 | 従来の形式 |
P004-http2 | 新しい形式 | 従来の形式 |
P005-http3 | 新しい形式 | 新しい形式 |
実行結果は同一で、ボタンを押すとJSONファイルを取得して、内容を表示します。
RxJSの新しい記述形式
まず、RxJSの新しい記述形式について、P003-http1サンプルと、P004-http2サンプルを比較しながら説明します。リスト6は、コンストラクターで依存性注入されたHTTP機能のオブジェクト(oldHttp)でダウンロード処理を行う実装です。従来は(1)のように、RxJSのインポートに独自の記述形式が必要でした。
import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; // 従来の形式 ...(1) (略) // Httpオブジェクトを受け取る public constructor(private oldHttp:Http){} (略) // HTTPダウンロード処理 this.oldHttp.get('/assets/sample.json') .map(res => res.json()) .map(obj => obj['message']) .subscribe(message => {this.message = message});
Angular 5では、リスト7(1)のように、ほかのimport同様「import {} from ~」形式で記述できるようになりました。この記述を利用するときは、(2)のようにpipeメソッドを利用してObservableチェーンを記述します。
import { Observable } from 'rxjs/Observable'; import { map } from 'rxjs/operators'; // 新しい形式 ...(1) (略) // Httpオブジェクトを受け取る public constructor(private oldHttp:Http){} (略) // HTTPダウンロード処理 this.oldHttp.get('/assets/sample.json').pipe( // Observableチェーン...(2) map(res => res.json()), map(obj => obj['message']) ) .subscribe(message => {this.message = message});
[参考]Observableチェーン
RxJSでは、特定の処理に対するイベントを発生する「Observable」で非同期処理を記述します。リスト6、7では、「ダウンロード内容をJavaScriptオブジェクトに変換して、その中からmessageを抽出する」一連の処理を、各処理に対応するObservableをつなげて記述することで実現しています。このような記法をチェーンと呼びます。
HttpClientの利用方法
次にHttpClientについて、P004-http2サンプルとP005-http3サンプルを比較しながら説明します。HttpClientを利用するには、まずルートモジュール定義ファイルapp.module.tsで、従来のHttpModuleのかわりにHttpClientModuleをインポートします。
//import { HttpModule } from '@angular/http'; // 従来のモジュール import { HttpClientModule } from '@angular/common/http'; // 新しいモジュール (略) @NgModule({ (略) imports: [ BrowserModule, // HttpModule, // 従来のモジュールではなく HttpClientModule // 新しいモジュールをインポート ], (略) }) export class AppModule { }
コンポーネントでは、リスト9のようにHttpClientを利用します。(1)のコンストラクターで依存性注入されたHttpClientオブジェクト(newHttp)を、(2)で利用してダウンロード処理を実行します。HttpClientの記述形式は従来のHTTP機能(Httpオブジェクト)とほぼ同じですが、レスポンスをJSONに変換する(3)の記述が不要になります(リスト7と比較してください)。
//import { Http } from 'angular/http' // 従来のHTTP機能 import { HttpClient } from '@angular/common/http'; // 新しいHTTP機能 (略) // HttpClientオブジェクトを受け取る ...(1) public constructor(private newHttp:HttpClient){} (略) // HTTPダウンロード処理 ...(2) this.newHttp.get('/assets/sample.json').pipe( // map(res => res.json()), // この処理は不要 ...(3) map(obj => obj['message']) ) .subscribe(message => {this.message = message});