Angular2の使い方
Angular2(beta12)の使い方について簡潔に説明します
細かい部分は省くので、公式サイト等を参考にしてください
あと、β版ということで情報が正しくなくなる可能性もありますがご了承ください
インストール
npm install angular2@2.0.0-beta.12
beta版のためか依存しているパッケージがインストールされないので同様に下記もインストール
npm install es6-shim@0.35.0 reflect-metadata@0.1.2 rxjs@5.0.0-beta.2 zone.js@0.6.6
コンパイル用のパッケージとかは必要に応じてインストールしてください
一応、下記リポジトリにQuickStartを作ってあるのでお試しください
手軽に試したい場合は、Plunkerもオススメです
https://github.com/kanatapple/angular2-webpack-quick-start
使い方
アプリケーションの起動
ES6のモジュール読み込みでbootstrapとルートコンポーネントを読み込み
bootstrap関数にルートコンポーネントを渡します
// main.ts import {bootstrap} from 'angular2/platform/browser'; import {App} from './app'; bootstrap(App, []) .catch(err => console.error(err));
// app.ts import {Component} from 'angular2/core' @Component({ selector: 'my-app', template: ` <div> <h2>Hello World</h2> </div> ` }) export class App { }
// HTML <body> <my-app> loading... </my-app> </body>
@ComponentはDecorators構文といってclassなどに付加情報を設定するものです
上記ではselectorとtemplateという情報を付与しています
selectorはquerySelectorなどに渡すCSSセレクタと同じものです
CSSセレクタにマッチするタグの中にアプリケーションが展開されます
例えば、selector: 'body'
とするとbodyタグの中に展開されることになります
templateはHTMLテンプレートです
この内容がCSSセレクタにマッチしたタグの中に展開されます
templateUrlとすると外部のテンプレートを設定することも可能です
バインディング
単方向バインディング
モデルからビューへの反映です
<h2>{{title}}</h2>
上記のようにすると、クラスに定義されたtitleプロパティの内容が反映されます
双方向バインディング
<input type="text" [(ngModel)]="title">
上記のようにすると、inputタグ等で入力した値がクラスに定義されたプロパティに反映されます
inputタグに入力した値がモデルに反映され、h2タグの中身も変わることが確認できると思います
イベントバインディング
下記のようにすると、clickなどのイベント発火時に関数を呼び出すことができます
@Component({ selector: 'my-app', template: ` <div> <button (click)="onClick()">Click Me</button> </div> ` }) export class App { private title: string = 'Hello World'; private onClick(): void { alert('Clicked!!'); } }
他にもタグの属性値にバインディングすることもできますが
公式サイトを参考にするとすぐに理解できると思いますので見てみてください
子コンポーネント
コンポーネントが子コンポーネントを扱うにはComponent Decoratorでdirectivesを設定し
templateに子コンポーネントで定義したCSSセレクタ(タグ)を追加します
@Component({ selector: 'my-app', template: ` <div> <h2>Parent Component</h2> <child-component></child-component> </div> `, directives: [ChildComponent] })
制御構文
ngIf、ngFor、ngSwitchなどがあります(ngSwitchの説明は省略)
ngIf
タグに*ngIf="条件式"
と記述すると、条件を満たす時にタグが出力されます
条件を満たさない時はタグ自体が出力されません
条件式の記述方法はJavaScriptと同様です
ngFor
<ul> <li *ngFor="#alphabet of alphabets; #idx = index">[{{idx}}]{{alphabet}}</li> </ul>
上記のように、タグに*ngFor="#変数名 of Iterableな変数"
と記述すると繰り返しタグが出力されます
#変数名
はLocal Template Variablesというもので、そのタグ、もしくは、子要素で使える変数です
また#変数名 = index
と記述すると、現在のインデックスが取得できます
DI(Dependency Injection)
いわゆる「依存性の注入」ってやつです
私がAngular2で一番良くできていると思っているのがこれです
注入される側
HelloServiceというクラスのインスタンスが注入されるとします
下記のように普通にクラスを定義します
export class HelloService { public hello(): string { return 'Hello Angular2!!'; } }
注入する側
Component Decoratorのprovidersに注入するクラス(Provider)を指定し
classのconstructorの引数で受け取るようにします
@Component({ selector: 'my-app', template: ` <div> <h2>{{hello}}</h2> </div> `, providers: [HelloService] }) export class App { private hello: string; constructor(private helloService: HelloService) { this.hello = helloService.hello(); } }
上記のようにすると、Appクラスがインスタンス化される時に
自動的にHelloServiceが注入されるようになります
インジェクトチェーン
この注入の仕組みですが、JavaScriptのprototypeチェーンに似ています
自身のprovidersにProviderがなければ、親のコンポーネントを探しに、親のコンポーネントになければ、さらに上に・・という感じに
そしてこのインジェクトチェーンの頂点はbootstrap関数の第2引数です
つまり、ここで指定されたProviderはどのコンポーネントでも使用できるということになります
bootstrap(App, [HelloService]);
上記で説明した以外にもDIについてはいろいろあるのですが
下記の記事が非常にわかりやすいので参考にしてみてください
http://qiita.com/laco0416/items/61eed550d1f6070b36ab
@Input
親のコンポーネントから子コンポーネントに値を渡したい時があると思います
そんな時に使うのが@Inputです
@Inputはコンポーネントに属性を定義するAPIです
export class ChildComponent { @Input() id: string; @Input('userName') name: string; }
上記のように@Inputで定義したものが属性として扱えます
属性名と内部の名前を変えたい場合は@Input('属性名')
とすると変えることが可能です
親コンポーネントで下記のようにします
@Component({ selector: 'my-app', template: ` <div> <child-component id="0" userName="Alice"></child-component> <child-component id="1" [userName]="name"></child-component> </div> `, directives: [ChildComponent] }) export class App { private name: string = 'Bob'; }
一番目のように値を直接指定して子コンポーネントに渡すこともできますし
二番目のように内部の変数の値を子コンポーネントに渡すことも可能です
また、子コンポーネントに値が渡ってくるタイミングですが
constructorではなくAngular2のライフサイクルのngOnChangesのタイミングなのでご注意を
※2016/04/15 ngOnInitと書いてましたが、ngOnChangesでした
まとめ
長々と書いてきましたが、これだけおさえておけばAngular2を使う時に悩むことはないと思います
@Outputという便利な機能がありますが、今回は省略しました
私はAngular1系をあまり触ってこなかったんですが、Angular2は非常によくできてるFWだと思います
是非お試しください!!
Github Pagesを使ったhttpsページの作り方
httpsのページを作るには証明書を置いたりしないといけないので
ちょっとめんどくさかったりします
Github pagesはSSLに対応しているので、簡単に作成することが可能です
Github Pagesではユーザー用とプロジェクト(リポジトリ)用の2種類が作れます
ユーザー用
ユーザー用のページはアカウントにつき1つ作れます
リポジトリ作成
(username).github.io
というリポジトリを作成します
READMEファイルは必要ないので、「Initialize this repository with a README」には
チェックはいれません
ファイル配置
次に、masterブランチに配置したいHTMLファイルをコミットします
↓こんな感じで
https://github.com/kanatapple/kanatapple.github.io
アクセスしてみる
ユーザー用のページは(username).github.io
というURLになるのでアクセスしてみます
https://kanatapple.github.io/
ちゃんとできてますね
プロジェクト用
プロジェクト用はリポジトリごとに作れます
リポジトリ作成
github-pages-sample
というリポジトリを作ってみます
READMEファイルについては「ユーザー用」と同様
ファイル配置
プロジェクト用では「gh-pages」というブランチにファイルを配置します
↓こんな感じで
https://github.com/kanatapple/github-pages-sample
masterブランチは必要ないので、作ってしまった場合は消してもOKです
アクセスしてみる
プロジェクト用のページは(username).github.io/リポジトリ名
というURLになるのでアクセスしてみます
https://kanatapple.github.io/github-pages-sample/
ちゃんとできてますね
まとめ
Generatorsについて
Symbol、Iteratorと順番に説明してきました
最後にGenerators(yield)について説明します
Generatorsとは
ジェネレータとはイテレータを強力にサポートするものです
例えば、イテレータを使って1から10まで順番に表示する場合
let array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; for (let n of array) { console.log(n); }
for文使ったり、関数化すればもっと簡潔に書けますが
イテレータを使うことが前提の場合こんな感じで書けます
ジェネレータを使うとスマートに書けます
function* gfn(from, to) { while(from <= to) { yield from++; } } var g = gfn(1, 10); for (var n of g) { console.log(n); }
上記のgfnがジェネレータ関数といい、gfnから得られるオブジェクトをジェネレータといいます
ジェネレータはIterableでもあり、Iteratorでもあります
g[Symbol.iterator] /* [Symbol.iterator]() { [native code] } */ g.next() /* Object {value: 1, done: false} */
ジェネレータ関数の書き方
- functionのあとに*(アスタリスク)を記述する
- yield、yield*を使う
function* gfn() { yield 1; yield 2; yield 3; } var g = gfn(); console.log(g.next()); // Object {value: 1, done: false} console.log(g.next()); // Object {value: 2, done: false} console.log(g.next()); // Object {value: 2, done: false} console.log(g.next()); // Object {value: undefined, done: true}
ジェネレータ関数を実行すると、ジェネレータを返すだけで関数自体は実行されません
nextメソッドが呼ばれると、次のyieldまで処理が進み IteratorResultが返され処理が一時停止します
最後まで処理が進むとdoneがtrueになります
ジェネレータの内部状態の変更
nextメソッドに値を渡すと、ジェネレータが一時停止する直前のyieldの結果となります
function* gfn() { var counter = 0; while (true) { var reset = yield counter++; if (reset) { counter = 0; } } } var g = gfn(); console.log(g.next()); // Object {value: 0, done: false} console.log(g.next()); // Object {value: 1, done: false} console.log(g.next()); // Object {value: 2, done: false} console.log(g.next(true)); // Object {value: 0, done: false} // trueを渡したので、yieldが結果がtrueとなりresetされた console.log(g.next()); // Object {value: 1, done: false}
yield*
yield*にはIterableなオブジェクトを渡すことが可能です
前回説明しましたが、String、Array、TypedArray、Map、SetなどがIterableなオブジェクトです
function* gfn() { yield 1; yield* [2, 3, 4]; yield* 'abc'; } var g = gfn(); console.log(g.next()); // Object {value: 1, done: false} console.log(g.next()); // Object {value: 2, done: false} console.log(g.next()); // Object {value: 3, done: false} console.log(g.next()); // Object {value: 4, done: false} console.log(g.next()); // Object {value: "a", done: false} console.log(g.next()); // Object {value: "b", done: false} console.log(g.next()); // Object {value: "c", done: false} console.log(g.next()); // Object {value: undefined, done: true}
yield*に渡された値を反復し、その値によってyieldを呼び出すイメージです 下記のような感じ
function* gfn() { var array = [2, 3, 4]; for (let n of array) { yield n; } }
また、冒頭でも書きましたが、ジェネレータはIterableでもあるのでyield*に渡せます
function* g1() { yield 1; yield* g2(); yield 5; } function* g2() { yield 2; yield 3; yield 4; } var g = g1(); console.log(g.next()); // Object {value: 1, done: false} console.log(g.next()); // Object {value: 2, done: false} console.log(g.next()); // Object {value: 3, done: false} console.log(g.next()); // Object {value: 4, done: false} console.log(g.next()); // Object {value: 5, done: false} console.log(g.next()); // Object {value: undefined, done: true}
ジェネレータを使った非同期処理
ジェネレータを使うと非同期処理を同期的に記述することができます
function async(gfn) { var g = gfn(); (function next() { var result = g.next(); if (!result.done) { result.value.then(next); } })(); } function sleep(msec) { return new Promise(function(resolve) { setTimeout(function() { resolve(); }, msec); }); } async(function* (){ console.log('start'); yield sleep(1000); console.log('finish'); });
yieldにPromiseを渡すことで、Promiseがresolveされるまで処理が待機されます
ただ、上の例だとPromiseに限定した使い方しかできないので
coという便利なライブラリを使った方が確実です
まとめ
- イテレータを強力にサポートするもの
- Iterableでもあり、Iteratorでもある
- functionのあとに*(アスタリスク)を記述する
- yield、yield*を使う
- 非同期処理を同期的に書ける
まだ自分でもそこまで把握できてない部分があるので、間違ってる箇所があればご指摘ください!
基本的な部分はおさえてあるかなって思ってます
Angular2+Webpack
Angular2の環境構築が結構面倒だったので
需要があるかわかりませんがQuickStart作ってみました
「Angular 2 Quick Start」って表示されるだけのものなので
必要に応じて肉付けしてください
lite-server使ってるんで
TypeScriptを更新すると自動でブラウザのリロードが走ります
Angular2をやりたいけど、環境構築がめんどくさいって方使ってみてください
Chromeアプリをブラウザから起動する
ブラウザからChromeアプリを起動する方法がわからなかったので調べてみた
url_handlersを使えばいいらしい(Chromeブラウザ限定です)
"url_handlers": { "url_handle_test": { "matches": [ "http://www.example.com/*" ], "title": "URL Handle Test" } }
上記のように設定しておく
この状態でhttp://www.example.com/
にアクセスするとChromeアプリが起動する
ブラウザから渡したパラメータをChromeアプリで使いたい場合は
chrome.app.runtime.onLaunched.addListenerに渡したcallbackの引数から取得できる
chrome.app.runtime.onLaunched.addListener(function(launchData) { });
http://www.example.com/?param=123456
にアクセスした時のlaunchData
launchDataのurlにアクセスした時のURLが渡されるので、後はゴニョゴニョすればOK
manifest.jsonで指定したtitleがどこで使われるのかは不明・・・