Angular 2 の RC が出たので試しにコンポーネントを書いてみます。

インスト―ル

@angular/ で始まるのが Angular2 本体です。ほかにもいろいろ必要なのでインストールしておきます。

ビルドは Browserify + babelify を使うことにします。 angular2 むけのプリセットがあるので簡単です。

$ npm install -g browserify
$ npm install -D babelify babel-preset-es2015 babel-preset-angular2
$ npm install -S @angular/common @angular/compiler @angular/core @angular/platform-browser @angular/platform-browser-dynamic reflect-metadata rxjs zone.js

簡単なコンポーネント

情報を表示するだけの簡単なコンポーネントを作ってみます。

// a.js

import 'reflect-metadata';
import 'zone.js';

import { Component } from '@angular/core';
import { bootstrap } from '@angular/platform-browser-dynamic';

@Component({
    selector: 'my-component',
    template: `
        <section>
            <h1>{{title}}</h1>
            <div>{{body}}</div>
        </section>
    `
})
class MyComponent {
    title = 'My Component';
    body = `
        Ipsum non officiis quis placeat impedit obcaecati? Ut nemo nulla error
        deserunt quo! Eum illum architecto excepturi quas quidem ab, veniam
        saepe iste. Asperiores consectetur harum id incidunt a! Delectus.
    `;
}

bootstrap(MyComponent);

Polyfill である reflect-metadata をはじめに読み込んでいます。自分の使い方が悪いのか zone.js も明示的に読み込まないとダメでした。

@Component(...)MyComponent がコンポーネントの定義であることを示します。 selector に CSS セレクタを、 template に文字通りテンプレートを渡します。

MyComponent クラスは表示に必要な情報を保持させます。

bootstrap(...) で実際のDOMのなかのコンポーネントを変換します。

これを bundle.js に変換します。 .babelrc を用意して browserify コマンドをたたきます。

{ "presets": ["es2015", "angular2"] }
$ browserify -t babelify -o bundle.js a.js

HTML で <my-component> 要素を配置しつつ bundle.js を読みこめばコンポーネントを利用できます。

<!DOCTYPE html>
<meta charset="utf-8">
<my-component></my-component>
<script src="bundle.js"></script>

コンポーネントを組み合わせて使う

コンポーネントの中でコンポーネントを使ってみます。文字を赤く表示する <my-red> を作ってみます。

// a.js

import 'reflect-metadata';
import 'zone.js';

import { Component } from '@angular/core';
import { bootstrap } from '@angular/platform-browser-dynamic';

@Component({
    selector: 'my-red',
    template: `
        <div style="color: red">
            <ng-content></ng-content>
        </div>
    `
})
class MyRed {}

@Component({
    selector: 'my-component',
    template: `
        <section>
            <h1>{{title}}</h1>
            <my-red>
                <div>{{body}}</div>
            </my-red>
        </section>
    `,
    directives: [MyRed]
})
class MyComponent {
    title = 'My Component';
    body = `
        Ipsum non officiis quis placeat impedit obcaecati? Ut nemo nulla error
        deserunt quo! Eum illum architecto excepturi quas quidem ab, veniam
        saepe iste. Asperiores consectetur harum id incidunt a! Delectus.
    `;
}

bootstrap(MyComponent);

ポイントは MyComponent のデコレータで directives: [MyRed] のように中で使うコンポーネントを渡して上げていることです。

<my-red> のテンプレートの <ng-content> は子要素に展開されます。

バインディング

Angular といえば双方向バインディングなので、それも試します。

// a.js

import 'reflect-metadata';
import 'zone.js';

import { Component } from '@angular/core';
import { bootstrap } from '@angular/platform-browser-dynamic';

@Component({
    selector: 'my-component',
    template: `
        <section>
            <input type="text" [(ngModel)]="text">
            <div>{{text}}</div>
        </section>
    `
})
class MyComponent {
    text = 'Text';
}

bootstrap(MyComponent);

これを表示するとテキストの入力欄が表示され、すぐ下に同じ内容が表示されます。入力欄を書き換えるとすぐに下の要素の内容も切り替わります。 Angular 1 のころからよくあるやつですね。

[(ngModel)]="..." が肝で、このように [(...)] で囲んだ属性は双方向バインディングになるみたいです。若干わかりにくく感じますが慣れですかね。

おわりに

Angular 2 を試してみました。 1 だと依存性を細かく制御するのがきつかったですが、 2 ではそこは結構考えて設計されていそうです。

ただし、慣れないとコードの意味がわかりづらく、学習コストは高いままな印象でした。