Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
AngularJS TIPS

AngularJS TIPS

AngularJSの依存性注入を利用するには?

2015年2月19日

AngularJSに標準搭載されているDIコンテナー機能を使って、依存性を注入するためのさまざまな方法を解説する。

  • このエントリーをはてなブックマークに追加
  • 対象:ng-strict-di属性については、AngularJS 1.3以降

 あるオブジェクトが別のオブジェクトを利用する場合、これを呼び出すためのコードをそのまま記述するのは、「オブジェクト同士が密に影響し合ってしまう」という意味で、望ましくないケースがあります。

 そこでよく利用されるのが、DI(Dependency Injection)コンテナーと呼ばれる仕組みです。DIコンテナーは、オブジェクト同士の依存関係を橋渡しするための仕掛け――オブジェクトが動作するのに必要なオブジェクトを外から引き渡すための機能、と言ってもよいかもしれません。依存性(Dependency)を注入する(Injection)、というわけです。

 AngularJSでは、標準で、このDIコンテナーの機能を備えています。

 例えば、別稿「TIPS:AngularJSでコントローラーを定義するには?」では、「コンストラクター関数にスコープオブジェクトが渡される」とさりげなく説明していますが、実はこれはすでにDIコンテナーのお世話になっています。

JavaScript
angular.module('myApp')
  .controller('myController', function($scope) {
    $scope.msg = 'Hello, AngularJS!';
  });
引数の名前でもってオブジェクトを引き渡す例*1
  • *1 完全なコードは、別稿のリストを参照してください。

 AngularJSでは、引数の名前でもって依存するオブジェクト*2を表す、という決まりがあります。よって、この例であれば、引数として$scopeを指定することで、AngularJSがあらかじめ用意していた$scopeサービス(=スコープオブジェクト)をコントローラーに引き渡しているわけです。

  • *2 サービス、と言います。

依存性注入のためのさまざまな方法

 オブジェクトを注入するには、他にも配列アノテーションを利用する方法と、$injectプロパティを利用する方法があります。それぞれについて見てみましょう。

(1)配列アノテーションを利用する

 controllerメソッドの第2引数に、[サービス名1, ……, コンストラクター関数]の配列を渡す方法です。以下は、既存のmyAppモジュールにmyControllerコントローラーを登録する例です。

JavaScript
angular.module('myApp')
  .controller('myController', ['$scope', function(s) {
    s.msg = 'Hello, AngularJS!';
  }]);
配列アノテーションを利用したコード

 これによって、「サービス名1, ……」の部分が順番にコンストラクター関数の引数に渡されることになります。

 先ほどのコードに比べると、コードがいくらか冗長になりました。しかし、実際のアプリでは、できるだけこの配列アノテーションを利用すべきです。

 なぜか。それは、実際のアプリでは本番リリースに際して、ダウンロード時間を節約するために、JavaScriptのコードを圧縮することが一般的だからです。JavaScriptの圧縮では、コメントや空白を除去するだけでなく、ローカル変数を短縮する手法がよく用いられます(例えば仮引数$scopeaなどに変換します)。

 そのため、引数名がサービス名を表す、先ほどの方法では、「開発環境では動作していたのに本番環境に移行した途端に動作しなくなった」という問題が発生し得るのです。しかし、文字列配列でサービス名を定義していれば、これは圧縮(短縮)の対象外ですので、上記のような問題は起こりません。

(2)$injectプロパティを利用する

 もう1つ、あらかじめ用意されたオブジェクト(=コンストラクター関数)に対して、$injectプロパティで注入すべきサービスを指定することもできます。

 先ほどのコードを、$injectプロパティを使って書き換えてみます。

JavaScript
var My = function(s) {
  s.msg = 'Hello, AngularJS!';
};
My.$inject = ['$scope'];
angular.module('myApp').controller('myController', My);
$injectプロパティを利用したコード

 配列アノテーションと同じく、$injectプロパティに渡された[サービス名, ……]が順番に、コンストラクター関数の引数に渡されます。

 まずは「(1)の記法を利用する」「引数記法は利用しない」の2点を基本として覚えておいてください。

依存性注入の厳密なチェック

 うっかり引数記法を利用してしまうのを防ぐために、AngularJS 1.3からはng-strict-di属性が追加されました。ng-strict-di属性は、ng-app属性と同じ要素(本連載のサンプルであれば、<html>要素)に指定します。これによって、誤って引数記法を利用してしまった場合にも、AngularJSがこれを「厳密に」チェックして、以下のように通知してくれます。

HTML
<!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>
ng-strict-di属性を有効にしたコード

 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)
ng-strict-di属性を有効にしたコードの実行結果
処理対象:コントローラー(Controllers) カテゴリ:基本
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]を参照してください。

AngularJS TIPS
1. AngularJSを利用するには?

クライアントサイド開発でも、本格的なアプリケーションフレームワークを利用しよう。世界中で人気爆発中のAngularJSの基本機能を目的別リファレンスの形式でまとめる連載スタート。

AngularJS TIPS
2. AngularJSでモジュールを定義するには?

コード規模が大きくなればモジュール化が必要になる。AngularJS専用として提供される「モジュール」の基礎を解説する。

AngularJS TIPS
3. AngularJSでコントローラーを定義するには?

AngularJSアプリの最も基本的な構成要素である「コントローラー」の基礎として、コントローラー経由でスコープを準備し、テンプレートに反映させる方法を説明する。

AngularJS TIPS
4. 【現在、表示中】≫ AngularJSの依存性注入を利用するには?

AngularJSに標準搭載されているDIコンテナー機能を使って、依存性を注入するためのさまざまな方法を解説する。

AngularJS TIPS
5. モデルをテキストボックスなどのフォーム要素にバインドするには?(ng-model)

ビューの変更をモデルに反映させ、逆にモデルの変更をビューに反映させる、AngularJSの双方向データバインディングの基本を解説する。デフォルト値の設定方法も説明。

サイトからのお知らせ

Twitterでつぶやこう!