🅰️

npm i angular2してHello World!するところまで

2021/07/04に公開

【160325 追記】この記事は Angular 2 alpha 時代に初稿を仕上げ、それ以降は変更にあわせて保守を続けていましたが、そろそろ差が大きくなってきたため改稿版として別の記事を用意しました。今後はそちらをご覧ください。


@armorik83 です。初日である昨日は、このアドベントカレンダー最大のサプライズとして@btford 氏に寄稿していただきました。アドベントカレンダーの立ち上げ直後からオファーし、日本の「アドベントカレンダー」という特殊な Tech 系の事情を説明し、Angular 2 のカレンダーをぜひ飾ってほしいという旨で依頼したことを覚えています。直前になって、時差で向こうはまだ 11 月なため日本のカレンダー開始に気付いていなかった、という笑い話も乗り越え、公開にこぎ着けたことを喜ばしく思います。

Thank you so much for contributing!


Angular 2 Advent Calendar 2015 の 2 日目の本日は、皆さんに手っ取り早く Angular 2 を触ってもらうために、npm iしてから Hello World を表示するところまでを解説します。

【151225 追記】本稿公開以降に Angular 2 の alpha バージョンが上がり、公開当初の内容では動かなくなってしまいました。現在は改稿し beta.0 に対応しています。

【160303 追記】現在は改稿し beta.8 に対応しています。

前提知識

npmを用いたインストール、package.jsonでの管理には慣れているものとします。今回はビルドにBrowserifyを使いますが、おそらくwebpackでもいけるはず。

Angular 2 はTypeScriptでのコーディングを前提に設計されているので、本稿も TypeScript を使用します。一応 ES6 (Babel)や ES5 でも書けるとは言われていますが、私は TypeScript で書くのが一番書きやすいと感じています。

今回の環境

細かなバージョン違いは影響しないと思いますが、npm だけ、2.x 系だとチュートリアル通りにいかない可能性があります。

$ node -v
v5.5.0
$ npm -v
3.6.0

公式サイト

開発環境を作る

sandbox

Angular 2 をインストールするディレクトリを作成します。これはなんでもかまいません。

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

インストール

下地となるpackage.jsonを作成します。ビルドの際にnpm runするためにこれが必要です。

npm init -y

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

npm i -D typescript browserify babelify
npm i -S angular2 es6-promise es6-shim@^0.33.3 reflect-metadata@0.1.2 rxjs@5.0.0-beta.2 zone.js@0.5.15

npm i -S angular2は本体のインストールです。npm 2 系ではnpm i -S reflect-metadata zone.jsが必要かもしれません。これも将来的には不要になる(Angular 2 が内部で依存するようになる)見込みのようです。

Angular 2 beta.8 時点では es6-promise 以下のモジュールも手動で指定してインストールする必要があり、それを満たさない場合 npm はUNMET PEER DEPENDENCYと警告を出します。今後 beta.2 以降で上記を試した時にもしUNMET PEER DEPENDENCYと表示されたらバージョン指定がズレている可能性があるので、エラー文を頼りに修正してください。

TypeScript の-t es6が指定できるにも関わらずes6-promise es6-shimが必須と指定されていますが、今後この辺りは整理されると予想しています。

ビルドスクリプト

TypeScript で書くためコンパイルが必要です。それぞれのファイルは TypeScript のimport/exportによって依存関係を記述するので、最終的には Browserify を使用します。

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

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

{
  "scripts": {
    "tsc": "tsc -p ./",
    "browserify": "browserify ./index.js -t babelify -o ./bundle.js",
    "build": "npm run tsc && npm run browserify"
  },
  "babel": {
    "presets": ["es2015"]
  },
}

tscでは、-t es5 -m commonjsとオプションを渡すのが初期のバージョンでおなじみでしたが、現在(TypeScript 1.5.3 以降)はtsconfig.jsonがサポートされていますので、-pオプションでプロジェクト・ディレクトリを指定しtsconfig.jsonを使っていくのが標準的な形式です。今回はサンプルなので雑にルートを指定しています。

従来通りコンパイラオプションを書くことも出来ますが、Angular 2 の場合は有効にすべきオプションが多いため、積極的にtsconfig.jsonを作成するほうがよいでしょう。オプションについては後述します。

browserifyは単純にエントリーソースを指定してbundle.jsとして出すだけ。buildでその二つの処理をまとめています。これくらいの量だとgulpは使っていません。

tsconfig.json

{
  "compilerOptions": {
    "target": "es6",
    "noImplicitAny": true,
    "removeComments": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "moduleResolution": "node"
  },
  "files": [
    "./index.ts"
  ]
}

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

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

ファイル作成

  • index.ts
  • index.html

この二つを作成します。

index.ts
// まだ何もない
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Angular 2 Advent Calendar 2015</title>
</head>
<body>
</body>
</html>

コーディング

Angular 2 を import

"angular2/*"から Angular 2 の様々な API を取得できます。今回は初回起動に必要なbootstrapとコンポーネント定義に使うComponentを import します。CORE_DIRECTIVESは今回は扱いませんが、Angular 2 の標準的な Directive が詰まったパッケージなので、import しておくとよいでしょう。

index.ts
import "reflect-metadata";
import "zone.js";
import "rxjs/Rx";

import {bootstrap} from "angular2/platform/browser";
import {Component} from "angular2/core";
import {CORE_DIRECTIVES} from "angular2/common";

このとき、beta.8 時点ではreflect-metadata, zone.js, rxjs/Rxの import も必要となります。

Component 定義

index.ts
import "reflect-metadata";
import "zone.js";
import "rxjs/Rx";

import {bootstrap} from "angular2/platform/browser";
import {Component} from "angular2/core";
import {CORE_DIRECTIVES} from "angular2/common";

@Component({
  selector: `my-app`,
  template: `
    <hello-world></hello-world>
  `,
  directives: [CORE_DIRECTIVES]
})
class MyApp {
}

今回は MyApp という Component を作るとします。class MyApp {}と書いた上に@Component()の Decorators を添えます。

class構文は JavaScript の印象と異なるかもしれませんが、JavaScript (ECMAScript)の次版である ES6 や TypeScript で、もはやお馴染みな構文です。一方で@を冠する Decorators 構文は ECMAScript には策定されておらず(proposal 審議中)TypeScript の仕様には含まれている構文です。これを本格的に扱うのは Angular 2 が初ではないでしょうか。

@Componentの引数オブジェクトは、AngularJS でいう Directive の定義オブジェクトのようなものです。

angular.module("myapp").directive("myDirective", function () {
  return {
    restrict: "E",
    link: function () {
      // ...
    },
  }; // この辺に似てる
});

selectorはセレクタ、ここでは Component 名を表します。Angular 2 の独自パーサのおかげでここをキャメルケースで記述することも可能になり、HTML 中では<foo-bar></foo-bar><FooBar></FooBar>のように書きます。 W3C の仕様には従うべきなので、要素名は小文字のハイフンで表記すべきです。

templateは見ての通りテンプレート HTML、ここはtemplateUrlとすることで外部ファイルのパスを指定できます。

directivesに与える配列は「この Component が扱う子 Directive および Component」を含むものです。directives: [CORE_DIRECTIVES]とすると十徳ナイフのように Angular 2 の便利 Directive がまとめて DI できます。(これについては別の機会に)

子 Component を作る

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

index.ts
import "reflect-metadata";
import "zone.js";
import "rxjs/Rx";

import {bootstrap} from "angular2/platform/browser";
import {Component} from "angular2/core";
import {CORE_DIRECTIVES} from "angular2/common";

@Component({
  selector: `hello-world`,
  template: `
    <h1>Hello World!</h1>
  `
})
class HelloWorld {
}

@Component({
  selector: `my-app`,
  template: `
    <hello-world></hello-world>
  `,
  directives: [CORE_DIRECTIVES, HelloWorld]
})
class MyApp {
}

このように、Angular 2 では Component を複数記述するのも簡単です。MyAppdirectives配列にHelloWorldを追加している点に注意してください。

ここでは一つのファイルにまとめていますが、実際のアプリケーション開発ではもちろん複数のファイルに分割し、互いにimport/exportしてください。

エントリーポイントを記述

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

index.ts
import "reflect-metadata";
import "zone.js";
import "rxjs/Rx";

import {bootstrap} from "angular2/platform/browser";
import {Component} from "angular2/core";
import {CORE_DIRECTIVES} from "angular2/common";

// 略

@Component({
  // ...
})
class MyApp {
}

bootstrap(MyApp);

bootstrap は、最初に表示される HTML と Angular 2 を繋ぐ大切な処理です。この引数に与える Component class は、Angular 2 アプリケーションのルートとなる Component です。HTML 側も追記します。

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

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

ビルド、そして起動

まずは完成形ソースを貼っておきます。

index.ts
import "reflect-metadata";
import "zone.js";
import "rxjs/Rx";

import {bootstrap} from "angular2/platform/browser";
import {Component} from "angular2/core";
import {CORE_DIRECTIVES} from "angular2/common";

@Component({
  selector: `hello-world`,
  template: `
    <h1>Hello World!</h1>
  `
})
class HelloWorld {
}

@Component({
  selector: `my-app`,
  template: `
    <hello-world></hello-world>
  `,
  directives: [CORE_DIRECTIVES, HelloWorld]
})
class MyApp {
}

bootstrap(MyApp);
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Angular 2 Advent Calendar 2015</title>
</head>
<body>
  <my-app>Loading…</my-app>
  <script src="./bundle.js"></script>
</body>
</html>

npmでビルドスクリプトを起動し、コンパイル&ビルドしましょう。

npm run build

少し待つとbundle.jsが出力されます。あとはindex.htmlをブラウザで開けば完了!(まだローカル経由で動きますが Router や Ajax などが絡むときはサーバを立ててください)

Screen Shot 2015-12-01 at 01.28.16.png

まだまだアプリケーションからは程遠いですが、基本的にいくつもの Component を細かく作っては View を組み立てていき、その後ろをドメイン層やストア層が支えるというアーキテクチャは変わりません。今回は基礎として Component の作り方を紹介しました。Angular 2 での本格的なアプリケーション開発については今後の記事・解説などをお待ち下さい。何よりまだ alpha 版ですからね!

実はもっと簡単にできる

ここにPlunkerがあるじゃろ?
Screen Shot 2015-12-01 at 01.32.19.png
これをこうして、
Screen Shot 2015-12-01 at 01.32.39.png
こうじゃ。


Plunker だとビルドも何も要らないのですげー簡単。みんな、これやろう。

Discussion