AngularJS開發指南15:AngularJS的創建服務,將服務注入到控制器,管理服務依賴詳解


創建服務

雖然AngularJS提供了很多有用的服務,但是如果你要創建一個很棒的應用,你可能還是要寫自己的服務。你可以通過在模塊中注冊一個服務工廠函數,或者通過Module#factory api或者直接通過模塊配置函數中的$provide api來實現。

所有的服務都符合依賴注入的原則。它們用一個唯一的名字將自己注冊進AngularJS的依賴注入系統(injector),並且聲明需要提供給工廠函數的依賴。它們的依賴在測試中可以是虛擬的,這使得它們能很好地被測試。

注冊服務

要注冊服務,你首先要有一個包含該服務的模塊。然后你就能通過模塊的api或者使用模塊配置函數中的$provide服務來注冊你的服務了。下面的偽代碼顯示了這兩種方法。

使用angular.Module api:

var myModule = angular.module('myModule', []);
myModule.factory('serviceId', function() {
  var shinyNewServiceInstance;
  //factory function body that constructs shinyNewServiceInstance
  return shinyNewServiceInstance;
});

使用$provide服務:

angular.module('myModule', [], function($provide) {
  $provide.factory('serviceId', function() {
    var shinyNewServiceInstance;
    //factory function body that constructs shinyNewServiceInstance
    return shinyNewServiceInstance;
  });
});

注意,你不應該注冊一個服務實例,而是一個會在被調用時創建實例的工廠函數。

依賴

服務不僅可以被依賴,還可以有自己的依賴。依賴可以在工廠函數的參數中指定。參閱AngularJS的依賴注入系統,和使用依賴的數組表示法和$inject屬性來讓依賴表示精簡化。

下面是一個很簡單的服務的例子。這個服務依賴於$window服務(會被當成參數傳遞給工廠函數),並且只是個函數。這個服務的任務是存儲所有的通知;在第三個通知以后,服務會用window的alert來輸出所有的通知。

angular.module('myModule', [], function($provide) {
  $provide.factory('notify', ['$window', function(win) {
    var msgs = [];
    return function(msg) {
      msgs.push(msg);
      if (msgs.length == 3) {
        win.alert(msgs.join("\n"));
        msgs = [];
      }
    };
  }]);
});

實例化AngularJS的服務

所有服務都是延遲實例化的。這意味着所有的服務只有在需要時,或者被依賴時才會實例化。換句話說,AngularJS不會實例化服務,除非被請求了或者被應用直接或間接依賴了。

要注意的是所有的AngularJS服務都是單例的。這意味着在每一個注入器中都只有一個需要的服務的實例。

將服務注入到控制器中

將服務用作控制器的依賴和將服務用作其他服務的依賴很類似。

顯式依賴注入

因為Javascript是一種動態語言,依賴注入系統無法通過靜態類型來知道應該注入什么樣的服務(靜態類型語言就可以)。所以,你應該用$inject的屬性來指定服務的名字,這個屬性是一個包含需要注入的服務的名字字符串的數組。名字要和服務注冊到系統時的名字匹配。服務的名稱的順序也很重要:當執行工廠函數時傳遞的參數是依照數組里的順序的。但是工廠函數中參數的名字不重要,但最好還是和服務本身的名字一樣。舉個例子:

angular.
 module('MyServiceModule', []).
 factory('notify', ['$window', function(win) {    //定義了一個服務notify
    var msgs = [];
    return function(msg) {
      msgs.push(msg);
      if (msgs.length == 3) {
        win.alert(msgs.join("\n"));
        msgs = [];
      }
    };
  }]);

function myController(scope, notifyService) {    //這里的scope是$scope,notifyService是notify服務
  scope.callNotify = function(msg) {
    notifyService(msg);
  };
}

myController.$inject = ['$scope','notify'];    //顯式定義依賴注入

隱式依賴注入

AngularJS依賴注入系統的新特性使得AngularJS可以通過參數名稱來判斷依賴。下面的例子展示一下隱式地依賴$window, $scope:

angular.
 module('MyServiceModuleDI', []).
 factory('notify', function($window) {
    var msgs = [];
    return function(msg) {
      msgs.push(msg);
      if (msgs.length == 3) {
        $window.alert(msgs.join("\n"));
        msgs = [];
      }
    };
  });

function myController($scope, notify) {   //通過參數名字,來隱式的定義依賴注入
  $scope.callNotify = function(msg) {
    notify(msg);
  };

但是如果你要壓縮你的代碼,你的變量名會被重命名,所以這時你就只能顯示地指定依賴了。

管理服務依賴

要聲明服務的依賴,你可以在工廠方法參數中隱式指明他們,也可以將$inject屬性設置成包含了依賴名稱的數組,或者是使用數組表示法。不推薦使用$inject屬性的這種方法。

使用數組表示法:

function myModuleCfgFn($provide) {
  $provide.factory('myService', ['dep1', 'dep2', function(dep1, dep2) {}]);     //依賴dep1,dep2服務
}

使用$inject屬性:

function myModuleCfgFn($provide) {
  var myServiceFactory = function(dep1, dep2) {};
  myServiceFactory.$inject = ['dep1', 'dep2'];    //依賴dep1,dep2服務
  $provide.factory('myService', myServiceFactory);
}

使用隱式依賴(使用代碼壓縮時會失效):

function myModuleCfgFn($provide) {
  $provide.factory('myService', function(dep1, dep2) {});
}

舉個例子來說明下服務之間的依賴:

  function batchLogModule($provide){
    $provide.factory('batchLog', ['$timeout', '$log', function($timeout, $log) {
        var messageQueue = [];
        function log() {
          if (messageQueue.length) {
            $log('batchLog messages: ', messageQueue);
            messageQueue = [];
          }
          $timeout(log, 50000);
        }
        log();
        return function(message) {
          messageQueue.push(message);
        }
    }]);
    $provide.factory('routeTemplateMonitor',
                ['$route', 'batchLog', '$rootScope',
               function($route,   batchLog,   $rootScope) {
                $rootScope.$on('$routeChangeSuccess', function() {
                    batchLog($route.current ? $route.current.template : null);
                });
             }
         ]
  ); }
angular.injector([batchLogModule]).get('routeTemplateMonitor');

上例中有幾點要注意:

  • batchLog服務依賴內建的$timeout和$log服務,並且允許消息批量地使用console.log記錄。
  • 和batchLog服務一樣,routeTemplateMonitor服務依賴內建的$route服務。
  • 自定義的服務都使用隱式表示和數組法來表示自己的依賴。最重要的是數組中的服務的名字順序要和工廠函數參數的名字順序對應。除非依賴是隱式地通過函數參數名表示的,那么就是有聲明依賴的數組名稱順序決定依賴注入的順序。

總結

AngularJS服務是一種能執行一個常見操作的單例,比如$http服務是用來操作瀏覽器的XMLHttpRequest對象的。

要使用AngularJS服務,你只需要在需要的地方(控制器,或其他服務)指出依賴就行了。AngularJS的依賴注入系統會幫你完成剩下的事情。它負責實例化,查找左右依賴,並且按照工廠函數要求的樣子傳遞依賴。

AngularJS web框架提供了一組常用操作的服務。和其他的內建變量或者標識符一樣,內建服務名稱也總是以"$"開頭。另外你也可以創建你自己的服務。

 

 

 

加油!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM