JavaScript
TypeScript
browserify
webpack
Angular2

npm iしてAngualr 2のHello World!を書くところまで【改】

More than 1 year has passed since last update.

追記160914: rc.7に対応しました、本題はこちらから。冒頭は終了したイベントの宣伝です。


@armorik83です。先日は私もng-japanに登壇してきましたが、会場内のAngular 2熱の高さに興奮を覚えたものです。

さて、私が代表を務めているng-kyotoとng-japan、GDG神戸、会場のteratailさん、サイボウズ株式会社さんのご協力によって、来る4/10と4/16にAngular 2ハンズオン勉強会を開催いたします。

これは両イベント共にng-kyotoの代表である私の他、@_likr, @shinsukeimai, @pastelInc オーガナイザらが関わっており、初のAngular 2ハンズオン東西合同開催と銘打って盛り上げております。さらにng-japanから強力なバックアップとして@can_i_do_web, @laco0416にもお手伝い頂いています。

ではなぜこれほどAngular 2を我々がプッシュするか。やはり、盛り上がってほしいからです!Angular 2はAngular 1とは打って変わってWeb標準にとても寄り添ったモダン・フレームワークです。先にWeb標準に沿って発表され一世を風靡したReactに対してAngular 1はお世辞にもモダンとは呼べませんでした。ところが2になり、ようやくイマドキらしい普通に選択しうるフレームワークとなったのです。

この辺りの、Angular 2が如何にモダンかという事情はlaco0416のAngular 2の失敗しない始め方を一読するのが間違いないでしょう。


本稿は同名の旧記事の改稿版です。先に告知したハンズオンでもAngular 2の導入はお伝えしていきますが、それとは別に「開発方法は見当がつくけれど最初が面倒くさい」という方も多いことでしょう。そんな方々にAngular 2を始めてもらえるよう"npm iするところから"紹介していくのが本稿の目的です。Angular 2はalpha, beta, rcと進むに連れて、初稿を書いた昨年12月からも様々な変化がありました。その差異を毎回の記事修正で補うには大きくなってきたことも、この改稿版を書いた理由です。


始めましょう

前置きが長くなりましたが、始めましょう。npmを用いたインストールや、package.jsonでの管理には慣れているものとします。今回はビルドにBrowserify, webpackを使います。両方の始め方を紹介するので、慣れている方はどちらかお好みのもので進めてください。Browserifyとwebpackのどちらを使えばいいか分からない方は、Node.jsの標準のモジュールシステムに互換性のあるBrowserifyを使ってください。

SystemJSを用いた方法は公式のチュートリアルに掲載されているため、この記事では触れません。

Angular 2はTypeScriptでのコーディングを前提に設計されているので、本稿もTypeScriptを使用します。ES2015やES5でも書けますが、私はTypeScriptで書くことをお勧めしています。


今回の環境

ビルド周りにしか使わないので、細かなバージョン違いは影響しないと思います。

$ node -v

v6.5.0
$ npm -v
3.10.7


公式サイト


開発環境を作る


sandbox

Angular 2をインストールするディレクトリを作成します。名前は任意です。ここではangular2-sandboxとします。

mkdir ~/Desktop/angular2-sandbox && cd $_


インストール

まずpackage.jsonを作成します。scriptsに記載した処理をビルドの際にnpm runで実行するために必要です。

npm init -y

続いてインストールを進めていきます。

npm i -S @angular/{core,common,compiler,platform-browser,platform-browser-dynamic} rxjs@5.0.0-beta.12 zone.js@0.6.21 core-js

npm i -D typescript browserify

webpack派の方は、npm i -Dに続けて次のように記述しましょう。

npm i -D typescript webpack

@angularで始まる複数のモジュールがAngular 2本体です。{}を用いて複数のモジュールをまとめてインストールすることができます。rxjszone.jsはPEER DEPENDENCYとなっており、これらは手動でのインストールが必須となります。面倒ではありますが、これらはブラウザに対してのpolyfillという位置付けのため、Angular 2が依存するライブラリではなくユーザが個々でインストールする形となっています。この設計の顛末はissuesにて確認できます。

core-jsはTypeScriptで出力したES5ソースの中でES2015 Promise, Collectionsを動かすために必要なpolyfillです。core-jsを含めずにtsc -t es6としてpolyfillを使わずに進めることも可能ですが、そのほかで煩雑な変換が求められるので本稿では割愛します。


ビルドスクリプト

TypeScript 1.8で書くため、ブラウザで動作するJavaScriptにするためのコンパイル作業が必要です。これから作成していく各.tsファイルはTypeScriptのimport/exportによって互いの依存関係を記述するので、最終的にはBrowserify, webpackを使用し結合します。

Angular 2公式のチュートリアルではSystemJSを推奨していますので、そちらを使っても構いません。どちらを使っても実装自体のimport/exportの書き方が変わるわけではありません。

package.json"scripts"を次のように書き換えます。


package.json

{

"scripts": {
"tsc": "tsc -p .",
"browserify": "browserify ./index.js -o ./bundle.js",
"build": "npm run tsc && npm run browserify"
}
}

webpackだと次のようになります。


package.json

{

"scripts": {
"tsc": "tsc -p .",
"webpack": "webpack ./index.js --output-filename ./bundle.js",
"build": "npm run tsc && npm run webpack"
}
}

TypeScriptのコンパイルコマンドtscでは、-pオプションでプロジェクト・ディレクトリを指定しtsconfig.jsonにコンパイルオプションを記述するのが標準的な形式です。これくらいの量だとgulpは使っていません。


tsconfig.json

tsconfig.jsonを作成します。

touch tsconfig.json

{

"compilerOptions": {
"target": "es5",
"noImplicitAny": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"moduleResolution": "node"
},
"filesGlob": [
"./**/*.ts",
"!./**/*.d.ts",
"!./node_modules/**/*",
"./node_modules/typescript/lib/lib.es6.d.ts"
],
"files": [
"./index.ts",
"./node_modules/typescript/lib/lib.es6.d.ts"
]
}

experimentalDecoratorsDecorators構文を有効にするためのオプションで、emitDecoratorMetadataは引数の型アノテーションを実装内でも扱うための出力を指定します。Angular 2の型ベースのDIのためにあるオプションだと認識しています。

それ以外のオプションについては公式のWikiを参照してください。filesはコンパイル対象のファイルを配列で与えますが、これを毎回手書きするのはとても煩雑なので、IDEの支援やCLIを活用して、人の手では書かないようにしましょう。

なお、現在はベータ版のため本稿では触れていませんが、TypeScript 2.0からはfilesの記述法が変わり、IDEの支援やCLIを導入することなく増加する.tsファイルに対応できるようになります。この方法についてはTypeScript 2.0 stableがリリースされたときに改稿する予定です。


HTMLの作成

初期ロード時に表示するHTMLを記述するindex.htmlを作成します。

touch index.html


index.html

<!DOCTYPE html>

<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Hello Angular 2!</title>
</head>
<body>
</body>
</html>


コーディング

エントリポイントとなるindex.tsNgModule(後述)を宣言するapp.module.ts、Componentを記述するapp.component.tsを作成します。

touch index.ts app.module.ts app.component.ts


index.tsにpolyfillをimportする

エントリポイントには、まずpolyfillのimport文も記述しておく必要があります。まずはここまでです。


index.ts

import 'core-js';

import 'zone.js/dist/zone';


NgModuleを宣言

Angular 2では、アプリケーションを開発していく際に、まずNgModuleという単位でclassを宣言し、そこにアプリケーション内で用いるコンポーネントやサービスを登録していくという流れで進めていきます。また、このNgModuleはライブラリ開発者にとっても、提供するひとつのパッケージの単位として扱うことができます。


app.module.ts

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

import { BrowserModule } from '@angular/platform-browser';

@NgModule({
imports: [
BrowserModule
],
declarations: [],
bootstrap: []
})
export class AppModule {}


importsは自分のNgModuleに他のNgModuleを取り込む際に使います。Angular 2が提供している各種モジュールや、サードパーティのモジュールをここに記述します。今回はAngular 2のBrowserModuleを使います。

declarationsには、DirectiveComponentPipeを登録していきます。

bootstrapには、アプリケーションのエントリポイントとなるComponentを指定します。


Component定義

続いてAngular 2アプリケーションのルートとなるComponentを定義していきます。Componentのためには@angular/coreからComponentをimportし、@Component()としてclass宣言の直上に記述します。


app.component.ts

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

@Component({
selector: 'my-app',
template: `
<hello-world></hello-world>
`

})
export class AppComponent {}


今回はAppComponentというComponentを作ります。export class AppComponent {}と書いた上に@Component()と添えます。これをDecorators構文といいます。

@を冠するDecorators構文はECMAScriptでは現在TC39 Proposal Stage 2、TypeScriptでは既に使用できる構文です。

@Component()内の各プロパティを見ていきましょう。selectorはセレクタ、ここではComponent名を表します。W3Cの仕様に従うべきなので、要素名はキャメルケースにせず小文字のハイフンで表記すべきです。templateは見ての通りテンプレートHTML、ここはプロパティ名をtemplateUrlとすることで外部ファイルのパスを指定できます。


子Componentを作る

Hello Worldを出力するComponentを作ってみましょう。前述のAppComponent内に書いてしまうこともできますが、せっかくなので親子構造をとってみます。

touch hello-world.component.ts


hello-world.component.ts

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

@Component({
selector: 'hello-world',
template: `
<h1>Hello World!</h1>
`

})
export class HelloWorldComponent {}


このように、Angular 2ではComponentを複数記述するのも簡単です。


NgModuleにdeclarationを追加

いま作成した2つのComponentを、先に作ったNgModuleに登録しましょう。


app.module.ts

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

import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { HelloWorldComponent } from './hello-world.component';

@NgModule({
imports: [
BrowserModule
],
declarations: [
AppComponent,
HelloWorldComponent
],
bootstrap: [AppComponent]
})
export class AppModule {}


declarationsに作成したComponentを追加していき、bootstrapにはルートとなるComponentのAppComponentを指定します。


エントリポイントを記述

最後にエントリポイントであるbootstrapModuleを記述します。


index.ts

import 'core-js';

import 'zone.js/dist/zone';

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule);


bootstrapModule()の引数に作成したNgModuleを渡します。最後にHTML側も追記します。


index.html

<!DOCTYPE html>

<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Hello Angular 2!</title>
</head>
<body>
<my-app>Loading…</my-app>
<script src="./bundle.js"></script>
</body>
</html>

<my-app>内は読み込み後にテンプレートに置き換わりますが、ここにローディング表示を書くことも可能です。その下、<script src="">ではBrowserify, webpackが出力したソースを指定します。


ビルド、そして起動

npmでビルドスクリプトを起動し、ビルドしましょう。

npm run build

少し待つとbundle.jsが出力されます。あとはindex.htmlをブラウザで開けば完了です!

Screen Shot 2015-12-01 at 01.28.16.png

この例はまだローカル経由で動きますが、RouterやAjaxなどが絡むときはローカルでサーバを立ててください。

今回の作成の経過はコミット単位でまとめてありますので参考にしてください。

https://github.com/armorik83/qiita-npm-i-angular2/commits/webpack


まだまだアプリケーションからは程遠いですが、基本的には、いくつものComponentを細かく作ってはViewを組み立てていき、その後ろをドメイン層やストア層が支えるというアーキテクチャです。Fluxを参考にするのも手でしょう。

今回は公式サイトで紹介されているSystemJSを使わずにBrowserify, webpackでAngular 2を始める方法を説明しました。Angular 2もいよいよstable真近となり毎日開発が進んでいますので、今から小さいアプリ開発にチャレンジして、今後の趣味、業務に活用しましょう!

それではまた。