🅰️

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

2021/07/04に公開

追記 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"を次のように書き換えます。

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

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

{
  "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
<!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 真近となり毎日開発が進んでいますので、今から小さいアプリ開発にチャレンジして、今後の趣味、業務に活用しましょう!

それではまた。

Discussion