AngularJS之Dependency Injection(五)

前言

這一節咱們來說講AngularJS中的依賴注入,在實際開發中Angular提供了具體的方法供咱們去調用,可是一旦業務不能知足要求或者出現麻煩或者錯誤時致使無從下手,因此基於此咱們有必要深刻一點去了解內部的基本原理,這樣咱們才能將Angular玩弄於鼓掌之間。css

話題

在講述依賴注入時咱們有必要講一講一個組件decorator(暫且叫作裝飾者)。它也是建立服務的一個例子。decorator是一種設計模式,它能隔離修改及在不修改源碼的前提下進行修改。在Angular中,它可以在服務、指令、過濾器使用以前被修改。html

咱們應該如何去使用使用裝飾者,咱們有兩種方法來註冊裝飾者 $provide.decorator 和 module.decorator 這兩種都有一個 $delegate ,這個被用來初始化服務、過濾器、指令。咱們主要介紹第一種註冊裝飾者的方法bootstrap

$provide.decorator

咱們看看在Angular中日誌服務是怎麼來的。設計模式

angular.module('myApp', [])

.config([ '$provide', function($provide) {

  $provide.decorator('$log', [
    '$delegate',
    function $logDecorator($delegate) {

      var originalWarn = $delegate.warn;
      $delegate.warn = function decoratedWarn(msg) {
        msg = 'Decorated Warn: ' + msg;
        originalWarn.apply($delegate, arguments);
      };

      return $delegate;
    }
  ]);
}]);

一旦日誌服務被初始化後,裝飾者(decorator)就會被觸發,decorator函數有一個注入到訪問在裝飾者中匹配所選擇的服務的$delegate對象。$delegate是咱們正在裝飾的服務對象,提供裝飾者返回的函數值將代替正在被裝飾的服務、過濾器、指令。簡單來說,經過decorator咱們可以在服務、過濾器或者指令使用以前進行適當的修改來知足咱們所需,也就是說這是最原始實例化服務的方法,咱們經過能夠此方法來打補丁或者重寫、修改等操做。在前面系列中咱們講了三種建立服務的方法,這節講述怎樣註冊組件而且注入他們以此來解析依賴。app

註冊組件

經過$provide服務來註冊例以下面服務以致於他們能被注入來知足依賴。這些組件是經過$injector而註冊(下面會講),當咱們請求一個服務時,經過$injector來找到正確的service provider。初始化服務提供者並經過調用$get工廠函數來獲取服務實例。經過$provide服務定義的許多服務方法被暴露在angular.module中。以下提供幾個有用的經過$provide服務定義的方法。ide

Name Descriptions
constant(name, value) 定義一個constant值
decorator(name, service) 定義一個服務decorator
factory(name, service) 定義一個服務
provider(name, service) 定義一個服務
service(name, provider) 定義一個服務
value(name, value) 定義一個值服務

如上除了decorator未被暴露在angular.module中,緣由猜測大概是:因爲是最原始的初始化服務的方法,因此在大部分狀況下咱們都不會用到,除非在特定狀況下要進行某一目的的特定修改咱們才須要,因此未進行顯式的暴露。在這裏咱們有必要來演示下。咱們在Angular原始日誌服務基礎上添加一點信息來進行打印。函數

界面代碼以下:

<head>  
    <meta charset="utf-8"/>
    <title>Angular Injection</title> 
    <link href="../Content/bootstrap.min.css"  rel="stylesheet"/>
    <script src="../Scripts/angular.min.js"></script>  
    <script src="../DependencyInjection/Decoration.js"></script> 
      
</head>  
<body ng-controller="indexController">  
    <div class="well">  
        <button class="btn btn-primary" ng-click="handleClick()">Click Me!</button>  
    </div>  
</body>  
</html>  

腳本代碼:

var testApp = angular.module('TestApp', []);  
  
testApp.controller("indexController", function ($scope, $log) {  
    $scope.handleClick = function () {  
        $log.log("Button Clicked");  
    };  
})
.config(function ($provide) {  
    $provide.decorator("$log", function ($delegate) {  
        $delegate.originalLog = $delegate.log;  
        $delegate.log = function (message) {
            $delegate.originalLog("經過原始Decoration方法打印: " + message);  
        }  
        return $delegate;  
    });  
});  

經過上述腳本咱們知道咱們只是打印了【Button Clicked】,可是咱們在使用日誌服務以前添加了【經過原始Decoration方法打印】。咱們看看效果:spa

管理組件 

上述咱們講到經過$injector來管理組件,究竟是怎麼管理組件呢?當咱們定義建立了組件時,咱們須要用到時則要用$injector來獲取咱們定義的組件。它能夠獲取例如類型、調用方法、加載模塊。下面咱們來看看$injector服務有關方法:設計

Name Descriptions
annotate(fn) 獲取指定服務函數的參數,同時也包括那些與服務未通訊的參數
get(name) 獲取指定服務名稱的服務對象
has(name) 判斷指定服務名稱的服務對象是否存在,若存在返回true,不然爲false
invoke(fn, self, locals) 調用指定函數, 使用指定函數的指定值以及非服務的參數值

$injector服務是AngularJS服務類庫的核心,可是咱們不多直接經過它來進行某些操做,可是咱們有必要而且是有用的經過它來了解AngularJS的工做原理。日誌

JS是一門動態並且牛逼的語言,可是仍是缺乏一點特性,好比在執行過程當中動做的註釋,而在C#中咱們能夠經過特性來看到,因此基於這種狀況,咱們來實現這種註釋狀況。

咱們只需將上述控制器代碼進行以下修改便可:

testApp.controller("indexController", function ($scope, $log, $injector) {  
    var counter = 0;  
    var logClick = function ($log, $exceptionHandler, message) {  
        if (counter == 0) {  
            $log.log(message);  
            counter++;  
        } else {  
            $exceptionHandler("Already clicked");  
        }  
    }  
    $scope.handleClick = function () {  
        var deps = $injector.annotate(logClick); //經過annotate方法來獲取logClick函數其參數 
        for (var i = 0; i < deps.length; i++) {  
            console.log("Dependency: " + deps[i]);  
        }  
    };  
})

獲取咱們logClick函數注入的服務以及參數,以下:

獲取服務經過injector

咱們來獲取根元素的$injector服務,咱們繼續修改控制器代碼,以下:

testApp.controller("indexController", function ($scope, $log, $rootElement) {  
    var counter = 0;  
    var logClick = function ($log, $exceptionHandler, message) {  
        if (counter == 0) {  
            $log.log(message);  
            counter++;  
        } else {  
            $exceptionHandler("不能再點擊啦,點爆啦,哥們!");  
        }  
    }  
    $scope.handleClick = function () {  
        var localVars = { message: "第一次點擊" };  
        $rootElement.injector().invoke(logClick, null, localVars);  
    };  
})  

$injector vs inject vs injector()

在咱們注入組件後,須要獲取該組件則能夠經過$injector.get("serviceName")來獲取所需服務,而inject一樣也能夠獲取咱們所需組件,以下:

    app.controller('indexCtrl', indexCtrl);

    indexCtrl.$inject = ['$scope','customService'];

    function indexCtrl($scope, customService) {
    .........
    }

上述inject獲取服務內部實質是經過$injector來獲取(經過查資料得知,不太肯定),經過$inject來獲取服務更方便且簡潔。上述還有一個injector方法,此方法用來獲取元素所在模塊的injector,經過 angular.element("id").injector() 來獲取。講到這裏,順便也講講scope方法,也是獲取元素所在的做用域,經過 angular.element("id").scope() 來獲取。

總結

今天咱們就講到這裏,休息! 

相關文章
相關標籤/搜索