这一节咱们来说讲AngularJS中的依赖注入,在实际开发中Angular提供了具体的方法供咱们去调用,可是一旦业务不能知足要求或者出现麻烦或者错误时致使无从下手,因此基于此咱们有必要深刻一点去了解内部的基本原理,这样咱们才能将Angular玩弄于鼓掌之间。css
在讲述依赖注入时咱们有必要讲一讲一个组件decorator(暂且叫作装饰者)。它也是建立服务的一个例子。decorator是一种设计模式,它能隔离修改及在不修改源码的前提下进行修改。在Angular中,它可以在服务、指令、过滤器使用以前被修改。html
咱们应该如何去使用使用装饰者,咱们有两种方法来注册装饰者 $provide.decorator 和 module.decorator 这两种都有一个 $delegate ,这个被用来初始化服务、过滤器、指令。咱们主要介绍第一种注册装饰者的方法bootstrap
咱们看看在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服务,咱们继续修改控制器代码,以下:
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.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() 来获取。
今天咱们就讲到这里,休息!