Marginalia

[Angular 4.0] core/commonモジュールの変更について

Angular 4.0新機能シリーズ第4弾です。今回はcoreモジュールとcommonモジュールに入った変更について解説します。

coreモジュールの変更

coreモジュールにはDependency Injectionとテンプレートシンタックスに変更が入っています。

OpaqueTokenの廃止とInjectionToken<T>の導入

ひとつめはDependency Injectionに関する変更です。 これまでInjectionのキーとして使われていたOpaqueTokenですが、4.0ではdeprecatedとなり、5.0で廃止されることになりました。 代わりに、InjectionTokenというクラスが導入され、今後はこちらを使うことになります。

InjectionTokenはInjectする対象の型情報をジェネリックとして持つことができます。 この変更に伴ってInjectorにも変更が入っており、第1引数がInjectionTokenのときは戻り値の型が推論されるようになっています。 たとえば文字列を注入するときは次のように書きます。

/** これまで **/

const APP_NAME = new OpaqueToken('appName');

{
    providers: [{ provider: APP_NAME, useValue: 'My Awesome App'}]
}

const appName = injector.get(APP_NAME) as string; // any型なのでキャストが必要

/** これから **/

const APP_NAME = new InjectionToken<string>('appName');

{
    providers: [{ provider: APP_NAME, useValue: 'My Awesome App'}]
}

const appName = injector.get(APP_NAME); // APP_NAMEから自動的にstring型になる

多くの場合、明示的なキャストが不要になり、簡潔なコードを書けるようになるでしょう。 もしユニットテストのモックなどで、InjectionToken<T>と互換性のないものを注入したいときは、逆に明示的なキャストがないとコンパイルエラーになります。

ライフサイクルメソッドのinterface化

これは一種の破壊的変更になりえるのですが、これまで抽象クラスとして提供されていたOnInitAfterViewInitなどが、インターフェースとして提供されるようになります。 公式ドキュメントやほぼすべてのガイドではimplements OnInitという書き方で解説しているのですが、もしextends OnInitと記述している人がいれば4.0からは動かなくなりますので注意してください。

<template>タグに関する仕様変更

これまでテンプレート中で<template>タグを使うとTemplateRefのインスタンスが生成され、再利用可能なテンプレート部品として使えるようにできました。 しかしその際にAngularは最終的なDOMの出力から<template>タグを除去してしまいます。 <template>タグはHTMLの標準要素であり、Web Componentsの普及や他のライブラリによるタグの利用もあるので、あまり好ましくない挙動でした。

これを解決するために、4.0では新しく<ng-template>というテンプレートシンタックスを導入しました。 これはこれまでの<template>タグとまったく同じ使い方で、TemplateRefへの参照を作成するためのタグです。 もちろんこれまでの<template>タグの利用も可能ですが、おそらく5.0アップデート時には完全な切り替えが行われます。 アプリケーション中で<template>タグを使っているところがあれば、早めに書き直しておきましょう。

ちなみに、2系と同じ<template>タグの動きを維持する挙動は無効にできます。 コンパイラのenableLegacyTemplateオプションがfalseに指定されていると、<template>タグは完全にAngularの干渉を受けなくなります。 JiTコンパイルのときはbootstrapModule関数の第2引数でコンパイラの設定ができます。

boostrapModule(AppModule, {
    enableLegacyTemplate: false
});

AoTコンパイルのときは、tsconfig.jsonやAoTPluginなどのangularCompilerOptionsで設定できます。

{
    "angularCompilerOptions": {
        "enableLegacyTemplate": false
    },
    "compilerOptions": {
    }
}

Renderer周辺の変更

4.0でもっとも大きな変更は、DOMレンダリングの内部機構の完全刷新です。 これまで使われていたRendererに代わるRenderer2がフルスクラッチで実装され、4.0からはそちらが使われています。 より高速で軽量になっていますが、これまでRendererの深いAPIを使っていた開発者にとっては僅かな修正が必要かもしれません。

RootRendererクラスは完全に廃止されていますので、代わりにRendererFactory2を使います。 なお、RendererクラスはまだInjectして使用できますが、非推奨になり、内部的にはRenderer2に処理を委譲しています。 今後はRenderer2を使うようにしましょう。

commonモジュールの新機能

NgIfの改善以外にも、commonモジュールにはいくつかの新機能が追加されました。

NgComponentOutletの導入

ダイアログのような用途で、コンポーネントを動的にテンプレートに追加したいケースで、これまではViewContainerなどのAPIを用いていましたが、 4.0から専用のNgComponentOutletというディレクティブが追加されて簡単に書けるようになります。

NgComponentOutletNgTemplateOutletと似ていて、次のように使います。

import { MyDialogComponent } from './my-dialog.component';

@Component({
    template: `
    <ng-container *ngComponentOutlet="dialogCmpType"></ng-container>
    `
})
export class MyCmp {
    dialogCmpType = MyDialogComponent;
}

注意としては、これまでと同様、動的に追加されるコンポーネントはNgModuleentryComponentsに設定するのを忘れないようにしましょう。

詳しいAPIについてはAPIドキュメントを参考にしてください。

titlecaseパイプの追加

これまでuppercaselowercaseしか用意されていませんでしたが、単語の先頭だけを大文字にするtitlecaseパイプが追加されました。

まとめ

  • OpaqueTokenからInjectionToken<T>
  • <template>から<ng-template>
  • NgComponentOutletで動的なコンポーネント生成
  • titlecaseパイプの追加
  • OnInitのインターフェース化
  • Rendererのバージョンアップ

Angular 4.0 Features