• AngularJS之Dependency Injection(五)


    前言

    这一节我们来讲讲AngularJS中的依赖注入,在实际开发中Angular提供了具体的方法供我们去调用,但是一旦业务不能满足要求或者出现麻烦或者错误时导致无从下手,所以基于此我们有必要深入一点去了解内部的基本原理,这样我们才能将Angular玩弄于鼓掌之间。

    话题

    在讲述依赖注入时我们有必要讲一讲一个组件decorator(暂且叫做装饰者)。它也是创建服务的一个例子。decorator是一种设计模式,它能隔离修改及在不修改源码的前提下进行修改。在Angular中,它能够在服务、指令、过滤器使用之前被修改。

    我们应该如何去使用使用装饰者,我们有两种方法来注册装饰者 $provide.decorator 和 module.decorator 这两种都有一个 $delegate ,这个被用来初始化服务、过滤器、指令。我们主要介绍第一种注册装饰者的方法

    $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我们能够在服务、过滤器或者指令使用之前进行适当的修改来满足我们所需,也就是说这是最原始实例化服务的方法,我们通过可以此方法来打补丁或者重写、修改等操作。在前面系列中我们讲了三种创建服务的方法,这节讲述怎样注册组件并且注入他们以此来解析依赖。

    注册组件

    通过$provide服务来注册例如下面服务以至于他们能被注入来满足依赖。这些组件是通过$injector而注册(下面会讲),当我们请求一个服务时,通过$injector来找到正确的service provider。初始化服务提供者并通过调用$get工厂函数来获取服务实例。通过$provide服务定义的许多服务方法被暴露在angular.module中。如下提供几个有用的通过$provide服务定义的方法。

    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方法打印】。我们看看效果:

    管理组件 

    上述我们讲到通过$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() 来获取。

    总结

    今天我们就讲到这里,休息! 

  • 相关阅读:
    位运算技巧2
    如果函数的参数是一个指针,不要指望用该指针去申请动态内存
    位运算 技巧1
    野指针?空指针?
    面试题:位操作实现四则运算
    面试题:递归颠倒栈 与栈排序
    求一个数任意位的值及位数
    基数排序
    面试题:最长回文子串(即求对称字符串的最大长度 )
    数据结构之后缀数组suffix array
  • 原文地址:https://www.cnblogs.com/CreateMyself/p/5537311.html
Copyright © 2020-2023  润新知