連載
» 2016年04月07日 05時00分 公開

MEANスタックで始めるWebアプリ開発入門(9):AngularJSの「サービス」で理解するDI(Dependency Injection:依存性注入)の基本 (2/2)

[中村修太,クラスメソッド]
前のページへ 1|2       

AngularJSでDIを使ってみよう

 登録した各サービスは他のサービスとの依存関係を持っている場合もあります。AngularJSではDIを使ってサービス同士の依存関係を簡単に記述できるので、それについて解説します。

そもそも、DIとは、そのメリットは

 AngularJSのDI機能について解説する前に、DIについて簡単に説明します。連載第6回の「いまさら聞けないAngularJSの基礎知識と5つの主な特徴、インストール、簡単な使い方」でも説明していますが、DIとは「Dependency injection(依存性の注入)」の略で、モジュール同士の依存関係をコードから除外し、設定ファイルやアノテーションなどで依存関係を記述できるようにするデザインパターンです。

 また、上記DIの機能を用いたフレームワーク/ライブラリ/機能のことを「DIコンテナ」と呼びます。

 基本的に、コンポーネント同士の関係はインタフェース(Javaの場合)を用いて抽象的に記述します。プログラム中に直接依存関係を記述せずに外部から依存関係を設定することで、下記のようなメリットがあります。

  • 単体テストがやりやすい
  • コンポーネント化が進み、メンテナンス性が向上

AngularJSのDI

 次に、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を設定する方法

 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はほぼ必須の機能となるので、ここで使い方を確認しておいてください。次回はカスタムディレクティブを紹介する予定です。

著者紹介

中村修太(なかむら しゅうた)

中村修太

クラスメソッド勤務の新しもの好きプログラマーです。数年前に東京から山口県に引っ越し、現在もリモート勤務しています。最近の趣味は空手とぬか漬け作り。


前のページへ 1|2       

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。