AngularJS TIPS
AngularJSの依存性注入を利用するには?
AngularJSに標準搭載されているDIコンテナー機能を使って、依存性を注入するためのさまざまな方法を解説する。
- 対象:
ng-strict-di
属性については、AngularJS 1.3以降
あるオブジェクトが別のオブジェクトを利用する場合、これを呼び出すためのコードをそのまま記述するのは、「オブジェクト同士が密に影響し合ってしまう」という意味で、望ましくないケースがあります。
そこでよく利用されるのが、DI(Dependency Injection)コンテナーと呼ばれる仕組みです。DIコンテナーは、オブジェクト同士の依存関係を橋渡しするための仕掛け――オブジェクトが動作するのに必要なオブジェクトを外から引き渡すための機能、と言ってもよいかもしれません。依存性(Dependency)を注入する(Injection)、というわけです。
AngularJSでは、標準で、このDIコンテナーの機能を備えています。
例えば、別稿「TIPS:AngularJSでコントローラーを定義するには?」では、「コンストラクター関数にスコープオブジェクトが渡される」とさりげなく説明していますが、実はこれはすでにDIコンテナーのお世話になっています。
angular.module('myApp')
.controller('myController', function($scope) {
$scope.msg = 'Hello, AngularJS!';
});
|
- *1 完全なコードは、別稿のリストを参照してください。
AngularJSでは、引数の名前でもって依存するオブジェクト*2を表す、という決まりがあります。よって、この例であれば、引数として$scope
を指定することで、AngularJSがあらかじめ用意していた$scope
サービス(=スコープオブジェクト)をコントローラーに引き渡しているわけです。
- *2 サービス、と言います。
依存性注入のためのさまざまな方法
オブジェクトを注入するには、他にも配列アノテーションを利用する方法と、$inject
プロパティを利用する方法があります。それぞれについて見てみましょう。
(1)配列アノテーションを利用する
controller
メソッドの第2引数に、[サービス名1, ……, コンストラクター関数]
の配列を渡す方法です。以下は、既存のmyApp
モジュールにmyController
コントローラーを登録する例です。
angular.module('myApp')
.controller('myController', ['$scope', function(s) {
s.msg = 'Hello, AngularJS!';
}]);
|
これによって、「サービス名1, ……」の部分が順番にコンストラクター関数の引数に渡されることになります。
先ほどのコードに比べると、コードがいくらか冗長になりました。しかし、実際のアプリでは、できるだけこの配列アノテーションを利用すべきです。
なぜか。それは、実際のアプリでは本番リリースに際して、ダウンロード時間を節約するために、JavaScriptのコードを圧縮することが一般的だからです。JavaScriptの圧縮では、コメントや空白を除去するだけでなく、ローカル変数を短縮する手法がよく用いられます(例えば仮引数$scope
をa
などに変換します)。
そのため、引数名がサービス名を表す、先ほどの方法では、「開発環境では動作していたのに本番環境に移行した途端に動作しなくなった」という問題が発生し得るのです。しかし、文字列配列でサービス名を定義していれば、これは圧縮(短縮)の対象外ですので、上記のような問題は起こりません。
(2)$injectプロパティを利用する
もう1つ、あらかじめ用意されたオブジェクト(=コンストラクター関数)に対して、$inject
プロパティで注入すべきサービスを指定することもできます。
先ほどのコードを、$inject
プロパティを使って書き換えてみます。
var My = function(s) {
s.msg = 'Hello, AngularJS!';
};
My.$inject = ['$scope'];
angular.module('myApp').controller('myController', My);
|
配列アノテーションと同じく、$inject
プロパティに渡された[サービス名, ……]
が順番に、コンストラクター関数の引数に渡されます。
まずは「(1)の記法を利用する」「引数記法は利用しない」の2点を基本として覚えておいてください。
依存性注入の厳密なチェック
うっかり引数記法を利用してしまうのを防ぐために、AngularJS 1.3からはng-strict-di
属性が追加されました。ng-strict-di
属性は、ng-app
属性と同じ要素(本連載のサンプルであれば、<html>
要素)に指定します。これによって、誤って引数記法を利用してしまった場合にも、AngularJSがこれを「厳密に」チェックして、以下のように通知してくれます。
<!DOCTYPE html>
<html ng-app="myApp" ng-strict-di>
……中略……
<script>
angular.module('myApp', []);
angular.module('myApp')
.controller('myController', function($scope) {
$scope.msg = 'Hello, AngularJS!';
});
</script>
|
IEのF12開発者ツールや、Chromeのデベロッパーツールなどで、[コンソール]/[Console]タブを開いた状態で、上記のコードのHTMLページを閲覧すると、そのタブ内に次のようなエラー情報が表示される。
Error: [$injector:strictdi] http://errors.angularjs.org/1.3.8/$injector/strictdi?p0=myController
at Error (native)
at https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js:6:416
at Vb (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js:34:113)
at Object.e [as invoke] (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js:36:439)
at z.instance (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js:76:210)
at https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js:59:164
at s (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js:7:408)
at v (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js:59:148)
at g (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js:52:9)
at g (https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js:52:26)
|
API:ngController(ng-controller) カテゴリ:ng(コアモジュール) > directive(ディレクティブ)
API:ngApp/ngStrictDi(ng-strict-di) カテゴリ:ng(コアモジュール) > directive(ディレクティブ)
API:angular.module カテゴリ:ng(コアモジュール) > function(関数)
API:angular.Module カテゴリ:ng(コアモジュール) > type(型)
API:$controllerProvider カテゴリ:ng(コアモジュール) > provider(プロバイダー)
※以下では、本稿の前後を合わせて5回分(第1回~第5回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
1. AngularJSを利用するには?
クライアントサイド開発でも、本格的なアプリケーションフレームワークを利用しよう。世界中で人気爆発中のAngularJSの基本機能を目的別リファレンスの形式でまとめる連載スタート。
3. AngularJSでコントローラーを定義するには?
AngularJSアプリの最も基本的な構成要素である「コントローラー」の基礎として、コントローラー経由でスコープを準備し、テンプレートに反映させる方法を説明する。
5. モデルをテキストボックスなどのフォーム要素にバインドするには?(ng-model)
ビューの変更をモデルに反映させ、逆にモデルの変更をビューに反映させる、AngularJSの双方向データバインディングの基本を解説する。デフォルト値の設定方法も説明。