登録した各サービスは他のサービスとの依存関係を持っている場合もあります。AngularJSではDIを使ってサービス同士の依存関係を簡単に記述できるので、それについて解説します。
AngularJSのDI機能について解説する前に、DIについて簡単に説明します。連載第6回の「いまさら聞けないAngularJSの基礎知識と5つの主な特徴、インストール、簡単な使い方」でも説明していますが、DIとは「Dependency injection(依存性の注入)」の略で、モジュール同士の依存関係をコードから除外し、設定ファイルやアノテーションなどで依存関係を記述できるようにするデザインパターンです。
また、上記DIの機能を用いたフレームワーク/ライブラリ/機能のことを「DIコンテナ」と呼びます。
基本的に、コンポーネント同士の関係はインタフェース(Javaの場合)を用いて抽象的に記述します。プログラム中に直接依存関係を記述せずに外部から依存関係を設定することで、下記のようなメリットがあります。
次に、AngularJSにおけるDIについて見てみましょう。
一般的なDIコンテナの場合、外部の設定ファイルやアノテーションを使ってDI設定を行います。AngularJSでDIを使用するには、変数名かアノテーションを使用してDIを実現します。
次の例を見てください。
var myApp = angular.module('mySimpleApp', []); //myServiceモジュールの登録 myApp.service('myService', function () { var service = { sayHello: function () { return "Hello!"; } }; return service; });
先ほども紹介した、service関数を用いたサービス定義です。mySimpleAppモジュールに対してservice関数を使用し、myServiceモジュールを登録しています。
次に、「myController」という名前でコントローラーを定義しています。
myApp.controller('myController', ['$scope', 'myService', function ($scope, myService) { $scope.greet = function () { //myServiceの関数を使用できる $scope.message = myService.sayHello(); }; } ]);
ここでは、controller関数の第2引数の配列でDIしたいサービス名を文字列で列挙し、最後にそれと同じ変数名を持った関数を定義します。こうすることで、$scope(ビルトインのサービス)とmyServiceがDIされ、コントローラー内で使用できるようになります。
なぜ名前を指定するだけでオブジェクトがDIされるかというと、AngularJSはコントローラーに指定された関数を文字列としてパースを行い、引数の変数名からDIすべきオブジェクトを判断しているためです。
AngularJSでDIをするには幾つか書き方があるので、それぞれの手法を見てみましょう(※myServiceモジュールは登録されている前提です)。
次のように、controller関数のコンストラクタ(第2引数)の引数として、直接サービス名を変数名として記述する方法でDIを行えます。
myApp.controller('myController',function ($scope, myService) { $scope.greet = function () { //myServiceの関数を使用できる $scope.message = myService.sayHello(); }; } );
一番シンプルな方法ですが、AngularJSのDIの動作原理的に、コードのminifyを行ってしまうと動作しなくなってしまいます。そのため、minifyの対策については後述する「配列アノテーション」を使用します。
DIしたい変数名の前後に「_」(アンダースコア)を付けた場合、それを付けなかった場合とまったく同じに扱われます(※下記サンプルの「_myService_」は、「myService」を指定した場合と同じ)。
myApp.controller('myController',function ($scope, _myService_) { var myService = _myService_; $scope.greet = function () { //myServiceの関数を使用できる $scope.message = myService.sayHello(); }; } );
関数はmyServiceとして利用可能になるため、外部スコープ内で定義した変数に割り当てることが可能になります。
先ほど、「関数の引数にサービス名を指定する形式ではminifyすると動作しない」と言いましたが、配列アノテーションを使った方法で回避できます。
この方法では、下記のようにDIするサービス名の文字列と関数を配列で渡します。文字列で渡すサービス名の順番とコンストラクタの引数の順番は一致させる必要があるので注意してください(※サービス名と引数名は一致させる必要はありません)。
myApp.controller('myController', ['$scope', 'myService', function ($scope, myService) { $scope.greet = function () { //myServiceの関数を使用できる $scope.message = myService.sayHello(); }; } ]);
アプリのリリース時にだけminify処理を行う開発スタイルの場合、「デバッグ時は問題ないがリリース時にのみ動作しない」という問題が発生する可能性があります。
そういった問題に対処するため、配列アノテーションを強制させるDIモードが導入されています(※AngularJS 1.3以降)。これを有効にするには、「ng-strict-di」属性をng-app属性と同じ要素に記述します。
<html ng-app='mySimpleApp' ng-strict-di> <body> ・ ・ ・
最初からng-strict-diを有効にしておけばminifyに関連するDIの不具合を事前に発見できるので、基本的には有効化しておいて方が良いと思います。
今回はAngularJSのサービスとDIについて解説しました。AngularJSを使用するうえで、サービスの定義とDIはほぼ必須の機能となるので、ここで使い方を確認しておいてください。次回はカスタムディレクティブを紹介する予定です。
Copyright © ITmedia, Inc. All Rights Reserved.