AngularJSの「サービス」で理解するDI(Dependency Injection:依存性注入)の基本:MEANスタックで始めるWebアプリ開発入門(9)(2/2 ページ)
MEANスタックを用いたWebアプリの開発方法について紹介していく連載。今回は、ビジネスロジックを実行するAngularJSの「サービス」の概要と種類、使い方に加え、DI機能の概要と設定方法を紹介する。
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はほぼ必須の機能となるので、ここで使い方を確認しておいてください。次回はカスタムディレクティブを紹介する予定です。
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- JavaScriptを中心としたWebアプリ開発の栄枯盛衰まとめ――LiveScriptからAngularJS/React.jsまで
@ITが誕生した2000年頃はJavaScriptが不遇だった時代。そこから現在のような人気のプログラミング言語になるまでには、どのような歴史があったのか。15周年を迎えた@ITの豊富なWeb開発関連記事とともに振り返る。 - Socket.IOでセンサー&MongoDB〜AngularJSアプリ間の通信を行う
家電〜Webアプリ間の双方向通信をSocket.IOで行うアプリについて、サーバー側のArduino連携やMongoDBへのデータ保存などと、クライアント側のAngularJSに分けて動作を解説します。 - エンタープライズ開発現場が知っておきたいHTML5の4つの意義
Windows XP時代にエンタープライズ向けシステムのクライアント開発現場が抱えていた問題は、HTML5(Web標準)という一つ上のレイヤーからアプローチし解決する道が模索されています。本記事では、先月開催されたカンファレンス「Enterprise × HTML5 Web Application Conference 2014」から幾つかのセッションの内容をピックアップし、HTML5ソリューションの全体像を俯瞰し、上記解決の道がどこに向かおうとしているのかを探ってみます。