カテゴリー別アーカイブ: サービス

AngularJS $httpサービスでのクロスオリジン通信

昨日の記事で, XMLHttpRequest Level 2を利用したクロスオリジン通信を記載しましたが, その方法はAngularJSでのクロスオリジン通信にも使えます.

ただし, Cookieを送信するために, 直接XMLHttpRequestインスタンスにアクセスすることはできないので, $httpサービスのオプションで指定します.

こんな感じです.

 $http({
     // ....,
     withCredentials : true,
     // ....
}).success(function(data, status, headers, config) {
     // do something ....
}).error(function(data, status, headers, config) {
     // do something ....
});

サーバーが返すレスポンスは昨日の記事で記載したままでOKです.

ただし, Ajaxを表すヘッダー, つまり, X-Requested-With: XMLHttpRequest を指定すると, クロスオリジン通信ができなくなるので注意してください.

$http({
     // ....,
     withCredentials : true,
     // ....,
     headers : {
          // いらない
          'X-Requested-With' : 'XMLHttpRequest'
     }
}).success(function(data, status, headers, config) {
     // do something ....
}).error(function(data, status, headers, config) {
     // do something ....
});

AngularJS カスタムサービスの作成 2

昨日の記事で, valueとconstantによるカスタムサービスの作成について記載しましたが, valueとconstantは他のサービスがインジェクションできないという制限がありました.

他のサービスをインジェクトしたサービスを作成したい場合は, (Moduleインスタンスの) factoryかserviceメソッドを利用します.

valueとconstantと同様, factoryとserviceの使い分けはと言いますと, ずばり, プリミティブ型 (数値, 文字列, 配列, プレインオブジェクト) を共通のサービスとして利用したい場合はfactoryメソッドを, クラス (コンストラクタ関数) をサービスとして利用したい場合はserviceメソッドを利用します.

var app = angular.module('app', []);

// factoryはプリミティブ型の登録が可能
app.factory('factoryService', ['$http', function($http) {
    var text = '';

    $http.get('sample.txt')
              .succes(function(data) {
                  text = data;
              });

    // 戻り値がサービスとして登録される
    return text;
}]);

angular.service('serviceClass', ['factoryService', function(factoryService) {
    this.model = factoryService;

     this.getModel = function() {
          return this.model;
     }
}]);

// カスタムサービスのインジェクション
angular.controller('Controller', ['$scope', 'serviceClass', function($scope, serviceClass) {
    // インスタンス化してインジェクションされる
    // シングルトンなので, インジェクションしたすべてのコントローラーやディレクティブで共通して参照される
    console.log(serviceClass.getModel());
}]);

AngularJS カスタムサービスの作成

AngularJSでカスタムサービス, つまり, コントローラーやディレクティブで共通に利用したい, 値や関数, オブジェクトを作成するためには, Moduleインスタンス (angular.moduleメソッドの戻り値) の以下のメソッドを利用します.

  • value
  • constant
  • factory
  • service
  • provider

この記事では,  最も単純なvalueとconstantについてまとめておきます.

valueとconstantの使いどころとしては, 他のサービスをインジェクションする必要がない場合です (逆の視点で表現すると, この2つのメソッドには他のサービスをインジェクションすることができません).

では, valueとconstantの使い分けは ? というのは, 登録したカスタムサービスをModule.configメソッドでも利用した場合は, constantメソッドを, そうでなければvalueメソッドを利用します.

Module.configメソッドは, 主に, 標準サービスのコンフィグレーションをするためのメソッドです. そのため, サービスのインスタンスが生成される前に呼び出されるメソッドなので, インジェクション可能なサービスが限定されるからです.

最後に, 利用例を記述しておきます.

var app = angular.module('app', []);

// 関数をサービスとして登録
// Module.configメソッド以外であればインジェクション可能
app.value('commonService', function() {
    return 'value';
});

// プレインオブジェクトをサービスとして登録
// Modue.configメソッドにもインジェクション可能
app.constant('options', {a : 'a', b : 'b});

// カスタムサービスのインジェクション
app.config(['options', function(options) {
    var a = options.a;  // -> 'a'
    var b = options.b;  // -> 'b'
}]);

app.controller('Controller', ['$sope', 'commonService', 'options', function($scope, commonService, options) {
     var r = commonService(); // -> 'value'
     var a = options.a; // -> 'a'
     var b = options.b; // -> 'b'
}]);

 

AngularJS 外部モジュールによるビューの更新

AngularJSでは, 双方向データバインディングによって, モデルを更新すれば即時にビューも更新されます

ところが, AngularJSが管理していない外部モジュールのイベントでモデルを更新しても即時にビューが更新されません. 更新されるのは次にAngularJSが管理しているモジュールでモデルを更新した場合です.

$scope.value = 0;

//jQuery UI Slider
$('#slider').sider({
    slide : function(event, ui) {
        //Update Model -> Not Update View ...
       $scope.value = ui.value;
    }
});

このままでは, jQuery UIやjQuery プラグインなどサードパーティ製モジュールが使いづらくなってしまいます.

しかしAngularJSにはこれを解決する手段が2つ定義されています.

  • $scope.$applyメソッドを利用する
  • $timeoutサービスを利用する
$scope.value = 0;

//jQuery UI Slider
$('#slider').sider({
    slide : function(event, ui) {
        $scope.$apply(function() {
            //Update Model -> Update View
            $scope.value = ui.value;
        });
    }
});
$scope.value = 0;

//jQuery UI Slider
$('#slider').sider({
    slide : function(event, ui) {
        $timeout(function() {
            //Update Model -> Update View
            $scope.value = ui.value;
        }, 0);
    }
});

$timeouサービスも内部的には$scope.$applyを利用しているので, 根本的にはどちらの方法も同じです.

しかし, $scope.$applyでは, 別のイベントによって更新処理が実行されているときに$scope.$applyメソッド呼び出しがあり, 更新処理が多重実行されてしまうと,

Error : {$rootScope:inprog] $digest already in progress

というエラーが出てしまいます.

$timeoutサービスではタイムアウト時間を0ミリ秒に設定することで, 更新処理の多重実行を回避して$scope.$applyを実行します.

AngularJS $http Ajaxのヘッダーを追加する

AngularJSでAjax通信をする場合, $httpサービスを利用するけれども, デフォルトではAjaxであることをサーバー側に伝えるヘッダーが付加されない.

もしサーバーサイドでリクエストヘッダーをチェックしてAjax以外のリクエストをはじいている場合などはAjaxのリクエストヘッダーを追加する必要があります.

$http({
    //....
    headers : { 'X-Requested-With' : 'XMLHttpRequest'},
    //....
}).success(function(datas, status, headers, config) {
    //do something ....
 }).error(function(datas, status, headers, config) {
    //do something ...
 });

といっても難しいことではなく, $httpサービスに指定するオブジェクトのheadersのキーに

{ 'X-Requested-With' : 'XMLHttpRequest'}

の連想配列を指定するだけです (もちろん, 複数のリクエストヘッダーを付加することは可能です).

{
 'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8',
 'X-Requested-With' : 'XMLHttpRequest'
 }