AngularJSアプリケーション開発ガイド 第7章 その他の便利な機能 1 $location

AngularJSアプリケーション開発ガイド

$location

$locationサービスは、window.locationをラップしたものである。$locationを利用する利点は以下のとおり。

  • 内部状態のグローバルな管理が不要
  • APIの提供
  • AngularJSとの統合
  • HTML5との統合

URLの変化に応答したい場合や、現在のURLを変化させたい場合にはいつでも$locationサービスを利用すべき。

$scope.apply()をいつどのように呼び出すべきかについては、ポイントが4つある。

  1. いつでも呼び出してよいというわけではない。行うべき処理がない場合に呼び出すと、例外が発生する。
  2. AngularJSの外側にあるコードがAngularJSの機能を利用している場合には、$scope.apply()を呼び出すべき。
  3. 可能なら、関数あるいはコードを実行してから$scope.apply()を呼び出すのではなく、その関数を$scope.apply()に渡すようにすべき。
  4. safeApplyなどの利用を検討する。

HTML5モードとHashbangモード

$locationサービスの設定は$locationProviderを通じて行う。$locationProviderには重要なプロパティが2つある。

  1. html5Mode
  2. hashPrefix

HTML5モードでは、HTML5のHistory APIを使ってブラウザ上のURLを操作する。$locationサービスはHTML5モードがサポートされているかどうかを自動的に判定し、未対応の場合はHashbangを使ったアプローチに切り替える。

以下の点に注意が必要。

  • サーバ側の設定:AngularJS上で使用されるURLに直接アクセスした場合のリダイレクト設定を行う
  • リンクの書き換え(以下のようなリンクの指定を行うとページが再描画される)
    • target属性が指定されている場合
    • 他のドメインへの絶対リンク
    • すでに定義されているものとは異なるベースURLからのリンク
  • 相対リンク:相対URLはHTMLの絶対URLを基準として解釈されるが、この絶対URLはアプリケーションが置かれたルートディレクトリとは異なることがある

AngularJSアプリケーションを実行する際には、ドキュメントルートを起点としてHistory APIを利用することが強く推奨される(相対リンクに関する問題がすべて解消されるため)。

次回は「AngularJSのモジュールのメソッド」から。

AngularJSアプリケーション開発ガイド 第5章 サーバとの通信 その2

AngularJSアプリケーション開発ガイド

RESTのリソースの扱い

$httpサービスはXHR(XMLHttpRequest)のリクエストを行うための低レイヤーの実装を提供している。

一方、ビジネスロジックの実装に際しては、オブジェクトモデルを理解し表現できるようなJavaScriptのオブジェクトが便利である。

このような機能を実現するのが、AngularJSの$resourceである。以下のような点が指定できる。

  • サーバ側でのリソースを表すURL
  • リソースへのリクエストでよく使われるパラメータの種類
  • get, save, query, remove, deleteに加え、オブジェクトモデルでのビジネスロジックや機能をカプセル化したメソッド
  • レスポンスとして期待されているデータ型
  • ヘッダー

なお、AngularJSの$resourceを使うべきなのは、サーバ側がRESTfulなふるまいをするWeb APIになっている場合だけである。REST以外の場合(JSON RPCなど)には、$resourceを使うべきではない。
(このためかは不明だが、$resource(ngResource)は組み込みのオブジェクトではなく、$route(ngRoute)と同様、外部モジュールとして提供されている)

$qとpromise

primiseとは:

  • 非同期形式のリクエストは、実際の戻り値の代わりにpromiseを返す
  • promiseにはthenという関数が用意される。thenは引数を2つ受け取る。1つはresolved、もう1つはrejected。これらには、実際の値あるいは失敗の理由のいずれかが渡される。
  • リクエストの結果が返されるとすぐに、どちらかのコールバックが必ず呼び出される

AngularJSの$qは:

  • AngularJSに対応しており、スコープのモデルに統合されている
  • AngularJSのテンプレートは$qをpromiseではなく実際の値として扱うことができる
  • AngularJSでは、promiseの機能のうち基本的かつ重要なものだけを採用しているため計量

レスポンスの横取り

サーバ呼び出しの度に何らかの処理(エラー処理、認証、有害なデータの除去など)を行いたい場合、レスポンスインターセプタという仕組みを利用できる。

セキュリティに関する注意点

JSONの脆弱性

対策は以下。

  • 重要な情報は必ずPOST形式のリクエストに対するレスポンスの中で、配列ではなくJSONオブジェクトで返す
  • 意図的に改変されたJSONデータをレスポンスとして返し、クライアント側でこれを実際のデータへと復元する

AngularJSはいずれの方法にも対応している。

XSRF(Cross-Site Request Forgery)

クライアント側でXHRのリクエストを行う際、cookieからXSRF-TOKENという名前のトークンを取り出してX-XSRF-TOKENというHTTPヘッダーにセットする(サーバ側にも対応が必要)。

AngularJSアプリケーション開発ガイド 第5章 サーバとの通信 その1

AngularJSアプリケーション開発ガイド

$httpを使った通信

HTTPのGETメソッドでリクエストを行う場合のコード例は以下。

リクエストの詳細な設定

$http.get()等に渡すconfigオブジェクトでは、以下のような項目について設定を行える。

method: HTTPリクエストの種類
url: リクエスト対象のリソース
params: キーと値のペア(URLのパラメータに変換される)
data: リクエストの本体
timeout: リクエストがタイムアウトするまでの時間(ミリ秒単位)

HTTPヘッダーの設定

すべてのリクエストに対してヘッダーを設定したい場合は以下のように書く。

特定のリクエストについてのみヘッダーを設定したい場合は以下のように書く。

レスポンスのキャッシュ

デフォルトではキャッシュは無効化されているが、以下のようにして有効化できる。

1度あるURLに対してリクエストが行われると、2回め以降は同じURLに対してキャッシュされたレスポンスが返される。

リクエストやレスポンスの変換

  • リクエストの際、configオブジェクトのdataプロパティにオブジェクトが含まれている場合、これをJSONに変換する
  • レスポンスにXSRFプレフィックスが含まれている場合、これを取り除く。
  • レスポンスがJSONであった場合はこれをオブジェクトへとデシリアライズする

次回は5.2 ユニットテストから。

AngularJSアプリケーション開発ガイド 第3章 AngularJSアプリケーションの開発

AngularJSアプリケーション開発ガイド

開発ツール(IDE)

WebStormがおすすめ(PhpStormはWebStormを内蔵しているので、PHP開発者はPhpStormにするとお得)。

  • コード補完、構文エラー等の表示、ライブラリ・フレームワーク対応、デバッガ統合
  • AngularJSプラグインがある

アプリケーションの実行

Yeomanを使う場合

「yeoman server」コマンドでWebサーバが起動でき、ライブリロードも実行される。
※本書執筆時点では、yeomanは「yeoman」コマンドで実行していたが、Yeoman 1.0.0以降ではyeomanコマンドは無くなっている

Yeomanを使わない場合

自前でWebサーバを立ち上げる必要がある。本文中ではnodeで簡易的なサーバを書く方法と、pythonでサーバを立てる方法が紹介されている。

PHPがインストールされているPC(最近のOS XにはPHPが同梱されている)なら、「php -S」で、カレントディレクトリをドキュメントルートにしてWebサーバを立ち上げることができる。localhostでポートが808番なら、「php -S localhost:8080」でサーバを立てて、http://localhost:8080/でアクセスできる。

AngularJSアプリケーションのテスト

テストランナーはKarmaがおすすめ。複数のブラウザ上で高速にテストが実行できる。

ユニットテスト

AngularJSはデフォルトではJasmineスタイルのテストに対応している。以下がコード例。

エンドツーエンドテスト

AngularJSにはシナリオランナーという仕組みが用意されている。これを使うと、ユーザーの操作をシミュレートできる。

シナリオランナーでは、Jasmineと同様の構文を使ってアプリケーションのふるまいを記述できる。同様のテスティングフレームワークとしてはSeleniumなどがあるが、AngularJS内臓のシナリオランナーは、さまざまな点でAngularJSにより適した機能をもっている。

コードのコンパイル(minify)

Closure Compiler等が利用できる。AngularJSで開発する上で特に注意が必要なのは、依存性注入の書き方について。

minifyツールは、変数をminifyすることはあっても、文字列をminifyすることはない、という性質を利用している。

minifyを絶対に使用しないならminifyすると動かない書き方でもよいが、そうでないならminifyしても動く書き方をすべき。

デバッグのコツ

  • デバッグを行う場合には必ず、最小化されていないバージョンのソースコードや依存先ライブラリを使用する
  • ソースコードはHTMLの中に埋め込まず、独立したJavaScriptファイルに記述する
  • ブレークポイントを利用する
  • ブラウザの「すべての例外で停止」オプションを利用してみる

Batarang

BatarangというChrome拡張を使用すると、AngularJSアプリケーションの内部状態等を可視化できる。

Yeoman(ワークフローの最適化)

Yeoman関係は本書執筆時点と現在では大きく変わっている。Yeomanは現在ではscaffoldingにのみ使用され、依存関係の解決はBower、タスクの実行はGruntGulpが担当する。

以下は、現時点(yeoman 1.3.3)での使い方。
※2015/04/29 generator-karmaが無いとWARNINGが出るのでgenerator-karmaを明示的にインストールするよう修正。環境:node.js v0.12.2、npm v2.7.4、yoeman v1.4.6、grunt-cli v0.1.13、grunt v0.4.5

Yeomanのインストール

AngularJSプロジェクトの新規作成

yoeman1.4.2現在、パッケージの依存管理はbowerで行う。yoemanではスキャッフォルドのためのテンプレートをジェネレータと呼んでいる。AngularJS用のジェネレータとして最も広く使われているのはgenerator-angular。また、テストランナーkarmaのためのジェネレータも必要(無くても動くが、WARNINGが出る)。

Sass入れる? とか聞かれるので適当に選択すればOK。angular-routeは入れておいたほうがよい(後述)。

サーバの実行

Gruntを使う場合(yo angularするとGruntfile.jsが自動生成されるので、Gruntを使うのが手軽)。

ルート、ビュー、コントローラの追加

※yo angular:routeを実行するには、yo angularの時点でangular-routeを選択してインストールしておく必要がある。インストールされていない場合は、再度yo angularを実行すればよい。

テスト

プロジェクトのビルド

  • すべてのJavaScriptファイルのコードを連結し、1つのファイルを生成する
  • ファイルのバージョン管理を行う
  • 画像を最適化する
  • アプリケーションキャッシュのためのマニフェストファイルを生成する

AngularJSとRequireJSの統合

RequireJSで依存先の定義と管理を行う。Bowerがパッケージマネージャなのに対して、RequireJSはモジュールごとに使用するJavaScriptを管理する、といった使い方のツール。割とめんどくさそうだけど、規模が大きくなると必要かも。

AngularJSアプリケーション開発ガイド 第2章 AngularJSアプリケーションの構造 その4

AngularJSアプリケーション開発ガイド

フィルタを使ったデータ形式の指定

フィルタを使うと、テンプレートの中で表示されるデータの表示形式を指定できる。フィルタの構文は次のとおり。

ここでexpressionはAngular式を表し、filterNameは適用したいフィルタの名前を表す。フィルタに与えるパラメータはコロンで区切って指定する。パラメータにもAngular式を指定できる。

組み込みのフィルタの1つ、currencyフィルタは次のように利用する。

フィルタの指定はコントローラやモデルではなくビューの中で行う。数値の前のドル記号は人間のユーザーにだけ意味があり、数値を処理するロジックにとっては重要ではない。

AngularJSには日付や数値あるいは大文字への変換といったフィルタも用意されている。

データバインディングの中でパイプ記号(|)を指定すると、複数のフィルタを連鎖させることができる。

上の例では、numberフィルタに四捨五入する桁数を指定し、12.9を13に変換している。

組み込みのフィルタだけでなく、自分で作成したフィルタを適用することもできる。フィルタの実装例は以下。

このフィルタは、以下のようにして呼び出せる。

サーバとの通信

サーバとの通信のため、$httpサービスが用意されている。単純なHTTPの他、JSONPとCORSにも対応し、JSONの脆弱性やXSRFに対する防御の機能も含まれている。リクエストやレスポンスのデータへの変換や、シンプルなキャッシュも可能。

サーバに問い合わせを行うコードの例は以下。

ディレクティブを使ったDOMの更新

ディレクティブは、HTMLの構文に対する拡張。独自の要素や属性を通じて、ふるまいやDOMへの変換を指定できる。再利用可能なUIコンポーネントを定義したりアプリケーションの設定を行ったりするほか、UIのテンプレートの中ではほぼどんな処理でも可能。AngularJSに組み込みで用意されているディレクティブだけでなく、自分でディレクティブを定義することもできる。

サービスと同様、ディレクティブもモジュールのAPIを通じて定義する。

入力データの検証

AngularJSアプリケーションでの<form>要素は自動的に機能拡張され、SPA(Single Page Application)に適した機能が追加される。

HTML5のrequired属性やtype=”email”、type=”number”などを使用すると、対応ブラウザではブラウザのネイティブ機能が呼び出され、非対応ブラウザでも同じ処理を行うディレクティブが自動的に追加される。
(このように、非対応のブラウザにも同等の機能を追加することをpolyfillと呼ぶ)

フォームのデータの正当性は、$validプロパティで確認できる。フォーム内のすべてのフィールドの値が正当な場合に、$validにtrueがセットされる。