xengineer’s diary

結果、メモ的な内容になっています。

angularjs2のtutorialをやってみた(その2)

お断り事項

本記事は、Angularjs 公式サイトのチュートリアルを

純粋にやってみているだけです。

英語苦手な方には悪くないと思いますが、本家のほうが、アップデートもかかるし、

英語問題ない方は本家をみたほうがよいです。

それと、5分クイックスタートをやってみたときの記事は以下から。

angularjs2のtutorialをやってみた - xengineer’s diary

概要

今回やるのは、下記リンクにある、"Tour of Heroes"というチュートリアルです。

Tutorial: Tour of Heroes - ts

きっと、前の5分のやつよりは色々わかることもあるのでしょう。

前提条件:

  • OS: Ubuntu 14.04.1 LTS
  • npm: 2.14.7
  • angularjs: 2.0.0-alpha.44
  • systemjs: 0.19.5
  • live-server: 0.8.1
  • typescript: 1.6.2
  • Linuxの操作には慣れている(開発にあたっての環境整備はできるレベル)
  • Angularjsの、5 Min Quickstart - ts、は理解できるレベル

さて、早速余談。僕はAngular2しか触ってないんだけど

Angular1.xとは結構変わっているところも多いみたい。

  • controllerがなくなった
  • directiveの使い方が簡単になった
  • angular.moduleがなくなった
  • $scopeがなくなった

などなど。angularの中の人の動画をみてたら、結構、Reactとか、fluxをみて、

「おー、これはいい」ってなって取り込んでるとこはあるぽい。

ざっくりフロー

本記事のざっくりした流れです。

  1. Tour of Heroesってなに?
  2. プロジェクトフォルダー作成
  3. TypeScript?
  4. 必須ライブラリのインストール
  5. 環境設定とタスク/httpd実行
  6. 動作イメージ
  7. Hero表示
  8. Hero編集
  9. Two-way binding
  10. TemplateDirective宣言
  11. FORM Directive宣言
  12. 実行

詳細

以降ではやったことを少し詳細に。

Tour of Heroesってなに?

そう、この謎タイトル。

どうやら、西洋のヒーローたちの一覧を作って、閲覧できるようにするツールらしいです。

以下スクショ。

ダッシュボード

f:id:xengineer:20151102175429p:plain

Magnetaさんの詳細編集

f:id:xengineer:20151102175435p:plain

ヒーローたち一覧

f:id:xengineer:20151102175441p:plain

作るサンプルにセンスを感じる。

こんなツールを作ります。がんばろう。

プロジェクトフォルダー作成

さ、フォルダ作り。

こんなフォルダ構成にしたい。

/
+- src
    +- app

下記コマンヅ実行。

$ mkdir -p angular2-tour-of-heroes/src/app
$ cd angular2-tour-of-heroes

記事内では、プロジェクトのroot directoryは、と呼びます。

では次へいきます。

TypeScript?

5分クイックスタートの記事を書いたときは、コード自体は、Javascriptで書いたんだけど、

今回は、ES6の便利機能も使ってみたいので、TypeScriptで書いてみよう。

凄い簡単にTypeScriptについて書くと、

マイクロソフトが開発した言語で、Javascriptのスーパーセット。

なので、実際に動かすときは、JSに変換しないといけない。

その変換を、「トランスパイル」っていう。

なので、前回の記事に加えて、Angular Componentを更新したら、トランスパイルして、JSに変換する、

っていう新たな手順も入ります。気をつけよっと。

必須ライブラリのインストール

まずは、インストール。下記コマンヅを実行します。

$ cd <WORKDIR>
$ npm init -y
$ npm i angular2@2.0.0-alpha.44 systemjs@0.19.5 --save --save-exact
$ npm i typescript live-server --save-dev

はて、systemjsとtypescriptは新しいので少し説明。

  • systemjs
    • require/import的にモジュールをロードする機能をjsに実装するためのライブラリ
  • typescript

これでライブラリのインストールは完了、と。

環境設定とタスク/httpd実行

今回は、TypeScriptのトランスパイラの設定と、httpdの設定をしますわい。

インストールコマンヅ実行後のpackage.jsonは、こんな感じ。

# package.json
{
  "name": "angular_test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "angular2": "2.0.0-alpha.44",
    "systemjs": "0.19.5"
  },
  "devDependencies": {
    "live-server": "^0.8.2",
    "typescript": "^1.6.2"
  }
}

何やら沢山。で、scriptセクションを書き換えます。

■変更前
"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1"
},

■変更後
"scripts": {
  "tsc": "tsc -p src -w",
  "start": "live-server --open=src"
},

tscってなんかいますね。

tscについて

tscっていうのは、TypeScriptの、トランスパイラ。

TypeScriptを、JSに変換してくれる。ナイス。

-p src: srcディレクトリ配下をトランスパイルしてくれる

-w : ファイルに変更があったかをずっと監視してくれる

で、tscのtranspile設定ファイル(tsconfig.json)を作る。

$ cd <WORKDIR>
$ vi src/tsconfig.json
{
  "compilerOptions": {
    "target": "ES5",
    "module": "commonjs",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false
  }
}

この辺は・・・そのうち勉強するということで・・・

これで、下記を実行すると、tscが変更のあったファイルを監視して、自動でトランスパイルしてくれるのと、

live-serverが立ち上がって、いつでもサンプルを確認できる状態に!

やっほーい。

$ npm run tsc

> angular_test@1.0.0 tsc /home/nemoto_hideaki/work/root4/angular_test
> tsc -p src -w

message TS6042: Compilation complete. Watching for file changes.

$ npm start

> angular_test@1.0.0 start /home/nemoto_hideaki/work/root4/angular_test
> live-server --open=src

Serving "/home/nemoto_hideaki/work/root4/angular_test" at http://127.0.0.1:8080

動作イメージ

インストールやら設定やら終わったところで、TypeScriptで書いた場合の、

全体の動作イメージをまとめてみます。

f:id:xengineer:20151104114039p:plain

書いてるうちに、gdgdになっちゃいましたが、、、

TypeScriptが入ったので、app.tsファイル(Angular ComponentをTypeScriptで書いたファイル)から、

app.jsに、変換する処理を、tscコマンドがやってるところが追加になっただけ、かな。

それにともなって、tscのコンフィグが追加になった、と。

Hero表示

ではやっと本題に入って、Heroの表示画面を。

ファイルを沢山作っていくけど、最終的なディレクトリ構造はこうなる。

angular2-tour-of-heroes
  +-- node_modules
  +-- src
  |    +-- app
  |    |    +-- app.ts
  |    |
  |    +-- index.html
  |    +-- tsconfig.json
  |
  +-- package.json

まずは、Angular componentをTypescriptで書きます。拡張子は、ts。

$ vi <WORKDIR>/src/app/app.ts

下記をぺったり貼り付ける。

# app.ts
import {Component, bootstrap} from 'angular2/angular2';
@Component({
  selector: 'my-app',
  template: '<h1>{{title}}</h1><h2>{{hero}} details!</h2>'
})
class AppComponent {
  public title = 'Tour of Heroes';
  public hero = 'Windstorm';
}
bootstrap(AppComponent);

次は、htmlファイル。

$ vi <WORKDIR>/src/index.html

下記をぺったり貼り付ける。

# index.html
<html>
  <head>
    <title>Angular 2 QuickStart</title>
    <script src="../node_modules/systemjs/dist/system.src.js"></script>
    <script src="../node_modules/angular2/bundles/angular2.dev.js"></script>
    <script>
      System.config({
        packages: {'app': {defaultExtension: 'js'}}
      });
      System.import('app/app');
    </script>
  </head>
  <body>
    <my-app>Loading...</my-app>
  </body>
</html>

で、tscとlive-serverは、再起動したほうがよさげ。

http://127.0.0.1:8080にアクセスしたら、

f:id:xengineer:20151104120814p:plain

こんなんでまーす。

Hero オブジェクト化

ここまでは、ツアーといっても、Windstormさんしかいない。

それにヒーローを追加するにしても、↓↓に毎回ヒーローを追加していかないといけなくて大変。

class AppComponent {
  public title = 'Tour of Heroes';
  public hero = 'Windstorm';
}

そのため、ヒーロークラスを作ってしまいましょう、というのが次。

app.tsに追記します。

# app.ts
import {Component, bootstrap} from 'angular2/angular2';
@Component({
  selector: 'my-app',
  template: '<h1>{{title}}</h1><h2>{{hero.name}} details!</h2>'
})

class Hero {      ★このclassを追記したよ
  id: number;
  name: string;
}
class AppComponent {
  public title = 'Tour of Heroes';
  public hero: Hero = { ★ここ変えたよ
    id: 1,
    name: 'Windstorm'
  };
}
bootstrap(AppComponent);

これで、http://127.0.0.1:8080にアクセス!!!

Loading...

ずっとこれが出るだけ・・・

Chrome Developer toolで見てみると、エラー!

angular2.dev.js:21835 EXCEPTION: Error during instantiation of Token Promise<ComponentRef>!.
angular2.dev.js:21835 ORIGINAL EXCEPTION: No Directive annotation found on AppComponentBrowserDomAdapter.logError @ angular2.dev.js:21835ExceptionHandler.call @ angular2.dev.js:4440(anonymous function) @ angular2.dev.js:19642run @ angular2.dev.js:138(anonymous function) @ angular2.dev.js:10644NgZone.run @ angular2.dev.js:10607ApplicationRef_.bootstrap @ angular2.dev.js:19615commonBootstrap @ angular2.dev.js:26650bootstrap @ angular2.dev.js:27475(anonymous function) @ app.ts:31(anonymous function) @ app.ts:31__exec @ system.src.js:1374entry.execute @ system.src.js:3535linkDynamicModule @ system.src.js:2933link @ system.src.js:2776execute @ system.src.js:3108doDynamicExecute @ system.src.js:715link @ system.src.js:908doLink @ system.src.js:569updateLinkSetOnLoad @ system.src.js:617(anonymous function) @ system.src.js:430run @ angular2.dev.js:138zoneBoundFn @ angular2.dev.js:111lib$es6$promise$$internal$$tryCatch @ angular2.dev.js:1507lib$es6$promise$$internal$$invokeCallback @ angular2.dev.js:1519lib$es6$promise$$internal$$publish @ angular2.dev.js:1490(anonymous function) @ angular2.dev.js:219run @ angular2.dev.js:138zoneBoundFn @ angular2.dev.js:111lib$es6$promise$asap$$flush @ angular2.dev.js:1301
angular2.dev.js:21835 ORIGINAL STACKTRACE:BrowserDomAdapter.logError @ angular2.dev.js:21835ExceptionHandler.call @ angular2.dev.js:4443(anonymous function) @ angular2.dev.js:19642run @ angular2.dev.js:138(anonymous function) @ angular2.dev.js:10644NgZone.run @ angular2.dev.js:10607ApplicationRef_.bootstrap @ angular2.dev.js:19615commonBootstrap @ angular2.dev.js:26650bootstrap @ angular2.dev.js:27475(anonymous function) @ app.ts:31(anonymous function) @ app.ts:31__exec @ system.src.js:1374entry.execute @ system.src.js:3535linkDynamicModule @ system.src.js:2933link @ system.src.js:2776execute @ system.src.js:3108doDynamicExecute @ system.src.js:715link @ system.src.js:908doLink @ system.src.js:569updateLinkSetOnLoad @ system.src.js:617(anonymous function) @ system.src.js:430run @ angular2.dev.js:138zoneBoundFn @ angular2.dev.js:111lib$es6$promise$$internal$$tryCatch @ angular2.dev.js:1507lib$es6$promise$$internal$$invokeCallback @ angular2.dev.js:1519lib$es6$promise$$internal$$publish @ angular2.dev.js:1490(anonymous function) @ angular2.dev.js:219run @ angular2.dev.js:138zoneBoundFn @ angular2.dev.js:111lib$es6$promise$asap$$flush @ angular2.dev.js:1301
angular2.dev.js:21835 Error: No Directive annotation found on AppComponent
    at new BaseException (angular2.dev.js:16034)
    at DirectiveResolver.resolve (angular2.dev.js:9953)
    at RuntimeMetadataResolver.getMetadata (angular2.dev.js:12685)
    at TemplateCompiler.compileHostComponentRuntime (angular2.dev.js:24877)
    at RuntimeCompiler_.compileInHost (angular2.dev.js:27391)
    at DynamicComponentLoader_.loadAsRoot (angular2.dev.js:13289)
    at di_1.provide.useFactory (angular2.dev.js:19433)
    at Injector._instantiate (angular2.dev.js:22778)
    at Injector._instantiateProvider (angular2.dev.js:22714)
    at Injector._new (angular2.dev.js:22704)

なんだなんだ・・・と色々試してみて、最終チュートをじっくり読んでみた。

Hero class作成の箇所に、こんなことが書いてある。

Hero object

At the moment, our hero is just a name. Our hero needs more properties. Let's convert the hero from a literal string to a class.

Create a Hero class with id and name properties. Keep this near the top of the app.ts file for now.

Keep this near the top of the app.ts file for now.

near ですよ。どのくらいトップの近くか言ってくれよ!というわけで、importのすぐ下の行に書いたらちゃんと動いた。

↓↓が最終形態。

# app.ts
import {Component, bootstrap} from 'angular2/angular2';

class Hero {    ★ここを上に持ってきた
  id: number;
  name: string;
}

@Component({
    selector: 'my-app',
    template: '<h1>{{title}}</h1><h2>{{hero.name}} details!</h2>'
})
class AppComponent {
  public title = 'Tour of Heroes';
  public hero: Hero = {
    id: 1,
    name: 'Windstorm'
  };
}
bootstrap(AppComponent);

もうちょい表示改善

現状だと、templateのどこにもdiv tag的なものがないので、ヒーロー追加すると、

どんどん横に表示が広がってしまって美しくない。

ので、適度にdiv/labelを入れて、見れるtemplateにしますよ、と。

# app.ts
import {Component, bootstrap} from 'angular2/angular2';

class Hero {
  id: number;
  name: string;
}

@Component({
    selector: 'my-app',
    template: '<h1>{{title}}</h1><h2>{{hero.name}} details!</h2><div><label>id: </label>{{hero.id}}</div><div><label>name: </label>{{hero.name}}</div>'★ここを変えたよ
})
class AppComponent {
  public title = 'Tour of Heroes';
  public hero: Hero = {
    id: 1,
    name: 'Windstorm'
  };
}
bootstrap(AppComponent);

横長、です。back-ticksで囲ったところは、改行OKだそうで、こんな感じにかけます。

template:`
  <h1>{{title}}</h1>
  <h2>{{hero.name}} details!</h2>
  <div><label>id: </label>{{hero.id}}</div>
  <div><label>name: </label>{{hero.name}}</div>
  `

これ組み込んだ版↓↓

# app.ts
import {Component, bootstrap} from 'angular2/angular2';

class Hero {
  id: number;
  name: string;
}

@Component({
  selector: 'my-app',
  template:` ★ここ変えたよ
    <h1>{{title}}</h1>
    <h2>{{hero.name}} details!</h2>
    <div><label>id: </label>{{hero.id}}</div>
    <div><label>name: </label>{{hero.name}}</div>
    `
})
class AppComponent {
  public title = 'Tour of Heroes';
  public hero: Hero = {
    id: 1,
    name: 'Windstorm'
  };
}
bootstrap(AppComponent);

今度はこんな感じに表示されまする。

f:id:xengineer:20151104123238p:plain

Hero編集

さて、ここまでは表示をいじってきたけど、ここからは、ヒーロー編集に入ります。

まずは、templateに、input tagいれて、入力できるところを作ります。

template:`
  <h1>{{title}}</h1>
  <h2>{{hero.name}} details!</h2>
  <div><label>id: </label>{{hero.id}}</div>
  <div>
    <label>name: </label>
    <div><input value="{{hero.name}}" placeholder="name"></div> ★ここ追記
  </div>
  `

これで、こんな感じにフォームが出てくる。

f:id:xengineer:20151104124417p:plain

この状態だと、入力しても、何も反映されないので、Two-way bindingして、

ちゃんと表示にも反映されるようにします。

Two-way binding

Two-way bindingとは??から。

Stackoverflowにあったので引用。

javascript - What is two way binding? - Stack Overflow

  • When properties in the model get updated, so does the UI.
    • モデルのプロパティがアップデートされると、UIの表示もアップデートされる
  • When UI elements get updated, the changes get propagated back to the model.
    • UIエレメントがアップデートされると、モデルにもそれが波及する

ということでした。one-wayだと、上記のどっちか、なんでしょうね。

Angular1.xは、Two-way binding対応だったものの、

ネットで調べた感じでは、Angular2.xでは、One-wayになるぽい記述が多かったんだけど・・・

チュートには、堂々と Two-wayで載っている・・・まぁチュートのとおりにやります。

NgModel directive

この、bindingを実現するのに、NgModel directiveを使います。

NgModelについては、下記URLに記載があるけど、、、読んでもそんなにすっと入ってこないです、はい。

もうちょい前段の知識が必要そうなので、一旦詳細は忘れて、「Two-way binding == NgModel」くらいで進もう。

Template Syntax - ts

まず、input tagを置き換える。

template:`
  <h1>{{title}}</h1>
  <h2>{{hero.name}} details!</h2>
  <div><label>id: </label>{{hero.id}}</div>
  <div>
    <label>name: </label>
    <div><input value="{{hero.name}}" placeholder="name"></div>    ★この行を削除して
    <div><input [(ng-model)]="hero.name" placeholder="name"></div> ★この行を追記する
  </div>
  `

この状態だと、http://127.0.0.1:8080にいってもなにもみれないのであしからず。

TemplateDirective宣言

上の状態だと、ng-model directiveが何者かを書いてないので、トランスパイルできないのかな?

なので、ng-modelが何者かをangularに教えてあげる。

(app.tsの)import文に、NgModelを追記。

import {bootstrap, Component, NgModel} from 'angular2/angular2';

更に、(app.tsの) @Component宣言のすぐ下に、当該Componentが利用するdirectiveを追記。

諸々盛り込んだ版↓↓

# app.ts
import {bootstrap, Component, NgModel} from 'angular2/angular2'; ★ここ変えたよ

class Hero {
  id: number;
  name: string;
}

@Component({
  selector: 'my-app',
  template:`
    <h1>{{title}}</h1>
    <h2>{{hero.name}} details!</h2>
    <div><label>id: </label>{{hero.id}}</div>
    <div>
      <label>name: </label>
      <div><input [(ng-model)]="hero.name" placeholder="name"></div> ★ここ変えたよ
    </div>
    `,
  directives: [NgModel] ★ここ追記
})
class AppComponent {
  public title = 'Tour of Heroes';
  public hero: Hero = {
    id: 1,
    name: 'Windstorm'
  };
}
bootstrap(AppComponent);

で、いつものURLをみてみると、、、エラー(Chrome Developer toolのコンソールにでる)。

ぎゃー。沢山。

EXCEPTION: No value accessor for '' in [null]
angular2.dev.js:21835 EXCEPTION: No value accessor for '' in [null]BrowserDomAdapter.logError @ angular2.dev.js:21835BrowserDomAdapter.logGroup @ angular2.dev.js:21846ExceptionHandler.call @ angular2.dev.js:4431(anonymous function) @ angular2.dev.js:19543NgZone._onError @ angular2.dev.js:10711errorHandling.onError @ angular2.dev.js:10630run @ angular2.dev.js:141(anonymous function) @ angular2.dev.js:10644zoneBoundFn @ angular2.dev.js:111lib$es6$promise$$internal$$tryCatch @ angular2.dev.js:1507lib$es6$promise$$internal$$invokeCallback @ angular2.dev.js:1519lib$es6$promise$$internal$$publish @ angular2.dev.js:1490(anonymous function) @ angular2.dev.js:219microtask @ angular2.dev.js:10670run @ angular2.dev.js:138(anonymous function) @ angular2.dev.js:10644zoneBoundFn @ angular2.dev.js:111lib$es6$promise$asap$$flush @ angular2.dev.js:1301
angular2.dev.js:21835 ORIGINAL EXCEPTION: No value accessor for ''BrowserDomAdapter.logError @ angular2.dev.js:21835ExceptionHandler.call @ angular2.dev.js:4440(anonymous function) @ angular2.dev.js:19543NgZone._onError @ angular2.dev.js:10711errorHandling.onError @ angular2.dev.js:10630run @ angular2.dev.js:141(anonymous function) @ angular2.dev.js:10644zoneBoundFn @ angular2.dev.js:111lib$es6$promise$$internal$$tryCatch @ angular2.dev.js:1507lib$es6$promise$$internal$$invokeCallback @ angular2.dev.js:1519lib$es6$promise$$internal$$publish @ angular2.dev.js:1490(anonymous function) @ angular2.dev.js:219microtask @ angular2.dev.js:10670run @ angular2.dev.js:138(anonymous function) @ angular2.dev.js:10644zoneBoundFn @ angular2.dev.js:111lib$es6$promise$asap$$flush @ angular2.dev.js:1301
angular2.dev.js:21835 ORIGINAL STACKTRACE:BrowserDomAdapter.logError @ angular2.dev.js:21835ExceptionHandler.call @ angular2.dev.js:4443(anonymous function) @ angular2.dev.js:19543NgZone._onError @ angular2.dev.js:10711errorHandling.onError @ angular2.dev.js:10630run @ angular2.dev.js:141(anonymous function) @ angular2.dev.js:10644zoneBoundFn @ angular2.dev.js:111lib$es6$promise$$internal$$tryCatch @ angular2.dev.js:1507lib$es6$promise$$internal$$invokeCallback @ angular2.dev.js:1519lib$es6$promise$$internal$$publish @ angular2.dev.js:1490(anonymous function) @ angular2.dev.js:219microtask @ angular2.dev.js:10670run @ angular2.dev.js:138(anonymous function) @ angular2.dev.js:10644zoneBoundFn @ angular2.dev.js:111lib$es6$promise$asap$$flush @ angular2.dev.js:1301
angular2.dev.js:21835 Error: No value accessor for ''
    at new BaseException (http://127.0.0.1:8080/node_modules/angular2/bundles/angular2.dev.js:16034:21)
    at _throwError (http://127.0.0.1:8080/node_modules/angular2/bundles/angular2.dev.js:25067:11)
    at Object.setUpControl (http://127.0.0.1:8080/node_modules/angular2/bundles/angular2.dev.js:25049:7)
    at NgModel.onChanges (http://127.0.0.1:8080/node_modules/angular2/bundles/angular2.dev.js:13743:18)
    at AbstractChangeDetector.ChangeDetector_AppComponent_0.detectChangesInRecordsInternal (eval at <anonymous> (http://127.0.0.1:8080/node_modules/angular2/bundles/angular2.dev.js:20415:14), <anonymous>:162:57)
    at AbstractChangeDetector.detectChangesInRecords (http://127.0.0.1:8080/node_modules/angular2/bundles/angular2.dev.js:20209:14)
    at AbstractChangeDetector.runDetectChanges (http://127.0.0.1:8080/node_modules/angular2/bundles/angular2.dev.js:20192:12)
    at AbstractChangeDetector._detectChangesInShadowDomChildren (http://127.0.0.1:8080/node_modules/angular2/bundles/angular2.dev.js:20259:14)
    at AbstractChangeDetector.runDetectChanges (http://127.0.0.1:8080/node_modules/angular2/bundles/angular2.dev.js:20196:12)
    at AbstractChangeDetector.detectChanges (http://127.0.0.1:8080/node_modules/angular2/bundles/angular2.dev.js:20183:12)BrowserDomAdapter.logError @ angular2.dev.js:21835ExceptionHandler.call @ angular2.dev.js:4444(anonymous function) @ angular2.dev.js:19543NgZone._onError @ angular2.dev.js:10711errorHandling.onError @ angular2.dev.js:10630run @ angular2.dev.js:141(anonymous function) @ angular2.dev.js:10644zoneBoundFn @ angular2.dev.js:111lib$es6$promise$$internal$$tryCatch @ angular2.dev.js:1507lib$es6$promise$$internal$$invokeCallback @ angular2.dev.js:1519lib$es6$promise$$internal$$publish @ angular2.dev.js:1490(anonymous function) @ angular2.dev.js:219microtask @ angular2.dev.js:10670run @ angular2.dev.js:138(anonymous function) @ angular2.dev.js:10644zoneBoundFn @ angular2.dev.js:111lib$es6$promise$asap$$flush @ angular2.dev.js:1301
angular2.dev.js:21835 ERROR CONTEXT:BrowserDomAdapter.logError @ angular2.dev.js:21835ExceptionHandler.call @ angular2.dev.js:4447(anonymous function) @ angular2.dev.js:19543NgZone._onError @ angular2.dev.js:10711errorHandling.onError @ angular2.dev.js:10630run @ angular2.dev.js:141(anonymous function) @ angular2.dev.js:10644zoneBoundFn @ angular2.dev.js:111lib$es6$promise$$internal$$tryCatch @ angular2.dev.js:1507lib$es6$promise$$internal$$invokeCallback @ angular2.dev.js:1519lib$es6$promise$$internal$$publish @ angular2.dev.js:1490(anonymous function) @ angular2.dev.js:219microtask @ angular2.dev.js:10670run @ angular2.dev.js:138(anonymous function) @ angular2.dev.js:10644zoneBoundFn @ angular2.dev.js:111lib$es6$promise$asap$$flush @ angular2.dev.js:1301
angular2.dev.js:21835 _Context {element: input, componentElement: my-app, context: AppComponent, locals: Object, injector: Injector…}

FORM Directive宣言

どうやら、NgModelを宣言しただけでは足りないようだ。(って、本家英文にも書いてある)

さすがに、NgModel使うにあたって必要になる directiveを全部探すのは骨が折れるので、、、

その辺は便利なまとめ方があるようで。

ng-model directiveを使うために、FORM_DIRECTIVESという便利配列が定義されてるので、

それをimportしちゃえばOK!だそうです。

つまり、

# app.ts
import {bootstrap, Component, FORM_DIRECTIVES} from 'angular2/angular2'; ★ここ変えたよ

class Hero {
  id: number;
  name: string;
}

@Component({
  selector: 'my-app',
  template:`
    <h1>{{title}}</h1>
    <h2>{{hero.name}} details!</h2>
    <div><label>id: </label>{{hero.id}}</div>
    <div>
      <label>name: </label>
      <div><input [(ng-model)]="hero.name" placeholder="name"></div>
    </div>
    `,
  directives: [FORM_DIRECTIVES] ★ここ追記
})
class AppComponent {
  public title = 'Tour of Heroes';
  public hero: Hero = {
    id: 1,
    name: 'Windstorm'
  };
}
bootstrap(AppComponent);

実行

なんとなんと。これで、http://127.0.0.1:8080 にアクセスしたら、

まずはこの画面。

f:id:xengineer:20151104142856p:plain

テキストフィールドを編集すると、、、、

f:id:xengineer:20151104142921p:plain

ちゃんとすぐに表示にも反映されましたとさ!便利!

ひゃっほー。長かったけど、終わり。

でもまだ先はあるぽい。

続きはまた別記事で~。

の、別記事↓↓↓ angularjs2のtutorialをやってみた(その3) - xengineer’s diary

メモ

あと、実はもう一箇所エラー出してたのでメモ。

結論、directiveの付け方間違ってた。エラーは↓↓これ。

EXCEPTION: TypeError: Cannot read property 'hostView' of undefined
angular2.dev.js:21835 EXCEPTION: TypeError: Cannot read property 'hostView' of undefinedBrowserDomAdapter.logError @ angular2.dev.js:21835BrowserDomAdapter.logGroup @ angular2.dev.js:21846ExceptionHandler.call @ angular2.dev.js:4431(anonymous function) @ angular2.dev.js:19543NgZone._onError @ angular2.dev.js:10711errorHandling.onError @ angular2.dev.js:10630run @ angular2.dev.js:141(anonymous function) @ angular2.dev.js:10644zoneBoundFn @ angular2.dev.js:111lib$es6$promise$$internal$$tryCatch @ angular2.dev.js:1507lib$es6$promise$$internal$$invokeCallback @ angular2.dev.js:1519lib$es6$promise$$internal$$publish @ angular2.dev.js:1490(anonymous function) @ angular2.dev.js:219microtask @ angular2.dev.js:10670run @ angular2.dev.js:138(anonymous function) @ angular2.dev.js:10644zoneBoundFn @ angular2.dev.js:111lib$es6$promise$asap$$flush @ angular2.dev.js:1301
angular2.dev.js:21835 STACKTRACE:BrowserDomAdapter.logError @ angular2.dev.js:21835ExceptionHandler.call @ angular2.dev.js:4433(anonymous function) @ angular2.dev.js:19543NgZone._onError @ angular2.dev.js:10711errorHandling.onError @ angular2.dev.js:10630run @ angular2.dev.js:141(anonymous function) @ angular2.dev.js:10644zoneBoundFn @ angular2.dev.js:111lib$es6$promise$$internal$$tryCatch @ angular2.dev.js:1507lib$es6$promise$$internal$$invokeCallback @ angular2.dev.js:1519lib$es6$promise$$internal$$publish @ angular2.dev.js:1490(anonymous function) @ angular2.dev.js:219microtask @ angular2.dev.js:10670run @ angular2.dev.js:138(anonymous function) @ angular2.dev.js:10644zoneBoundFn @ angular2.dev.js:111lib$es6$promise$asap$$flush @ angular2.dev.js:1301
angular2.dev.js:21835 TypeError: Cannot read property 'hostView' of undefined
    at tick (angular2.dev.js:19626)
    at Zone.run (angular2.dev.js:138)
    at Zone.run (angular2.dev.js:10644)
    at zoneBoundFn (angular2.dev.js:111)
    at lib$es6$promise$$internal$$tryCatch (angular2.dev.js:1507)
    at lib$es6$promise$$internal$$invokeCallback (angular2.dev.js:1519)
    at lib$es6$promise$$internal$$publish (angular2.dev.js:1490)
    at angular2.dev.js:219
    at microtask (angular2.dev.js:10670)
    at Zone.run (angular2.dev.js:138)

間違ってたのはここ。

■ 間違い
<div><input [(ng-model)]="{{hero.name}}" placeholder="name"></div>

■ 正しい
<div><input [(ng-model)]="hero.name" placeholder="name"></div>

curly bracketつけっぱなしにしちゃってたのがいけなかった。

直したら無事表示されましたとさ。(記事内では正しいほうに直してます)