在Angular中创建一个对象时,需要依赖另一个对象,这是代码层的一种依赖关系,当这种依赖被声明后,Angular通过injector
注入器将所依赖的对象进行注入操作。
一、依赖注入的原理
看下面的示例代码:
1 <div ng-controller="MyController"> 2 <div class="{{cls}}">{{show}}</div> 3 <button ng-click="onClick()">点我</button> 4 </div>
1 var myapp = angular.module('MyApp', []); 2 myapp.config(function($controllerProvider) { 3 $controllerProvider.register('MyController', ['$scope', function($scope) { 4 $scope.cls = ''; 5 $scope.onClick = function () { 6 $scope.cls = 'show'; 7 $scope.show = '点击后显示的内容'; 8 }; 9 }]); 10 });
看上面的代码,与我们平时定义控制器的方式不一样
1 myapp.controller('MyController', ['$scope', function($scope){ 2 // 控制器代码 3 }]);
在Angular中,通过模块的config函数来声明需要注入的依赖对象,而声明的方式是通过调用provider服务。
provider
服务来创建的,而是由controllerProvider
服务创建,在创建过程中,实际上是在config函数中调用controllerProvider
服务的register
方法,再调用injector
注入器完成各个依赖对象的注入。二、简单依赖注入的示例
$provide
服务,其中它包含了如provider
方法、factory
方法、service
方法和value
方法,这些方法都可以通过服务创建一个自定义的依赖注入对象。示例:
1 <div ng-controller="myCtrl"> 2 <div class="{{cls}}">{{text}}</div> 3 <button ng-click="onClick(1)">早上</button> 4 <button ng-click="onClick(2)">上午</button> 5 <button ng-click="onClick(3)">下午</button> 6 <button ng-click="onClick(4)">晚上</button> 7 </div>
1 var app = angular.module('myApp', []); 2 app.config(function ($provide) { 3 $provide.provider('myprovider', function () { 4 this.$get = function () { 5 return { 6 val: function (name) { 7 return name; 8 } 9 } 10 } 11 }) 12 }); 13 app.config(function ($provide) { 14 $provide.factory('myfactory', function () { 15 return { 16 val: function (name) { 17 return name; 18 } 19 } 20 }) 21 }); 22 app.config(function ($provide) { 23 $provide.value('myvalue', function (name) { 24 return name; 25 }) 26 }); 27 app.config(function ($provide) { 28 $provide.service('myservice', function () { 29 return { 30 val: function (name) { 31 return name; 32 } 33 } 34 }) 35 }); 36 app.controller('myCtrl', ['$scope', 'myprovider', 'myfactory', 'myvalue', 'myservice', 37 function ($scope, myprovider, myfactory, myvalue, myservice) { 38 $scope.cls = ''; 39 $scope.onClick = function (t) { 40 $scope.cls = 'show'; 41 switch (t) { 42 case 1: $scope.text = myprovider.val('早上好'); break; 43 case 2: $scope.text = myfactory.val('上午好'); break; 44 case 3: $scope.text = myvalue('下午好'); break; 45 case 4: $scope.text = myservice.val('晚上好'); break; 46 } 47 } 48 }]);
三、依赖注入标记
每个Angular应用都是通过由注入器(injector
)负责查找和创建依赖注入的服务,当注入器执行时,它需要一些标记,来判断需要注入什么样的依赖服务,而这些标记,就是依赖注入标记。
依赖注入标记根据声明的方式,分为:
——推断式注入
——标记式注入
——行内式注入
1.推断式注入
推断式注入是一种猜测式的注入,在没有明确声明的情况下,Angular认为参数名称就是依赖注入的函数名,并在内部调用函数对象的toString
方法,获取对应的参数列表,然后通过注入器(injector
)将这些参数注入到应用实例中,从而实现依赖注入。
示例:
1 <div ng-controller="myCtrl"> 2 <input type="button" value="弹出对话框" ng-click="onClick('我是一个弹出对话框')"> 3 </div>
1 var app = angular.module('myApp', []); 2 app.factory('myfactory', function ($window) { 3 return { 4 show: function (text) { 5 $window.alert(text); 6 } 7 } 8 }); 9 var myCtrl = function ($scope, myfactory) { 10 $scope.onClick = function (msg) { 11 myfactory.show(msg); 12 } 13 }; 14 app.controller('myCtrl', myCtrl);
2.标记式注入
标记式注入明确了一个函数在执行过程中需要依赖的各项服务,它可以直接调用$injector
属性来完成,它的属性值是一个数组,数组元素是需要注入的各项服务的名称,所以这种方式的注入顺序很重要。
示例:
1 <div ng-controller="myCtrl"> 2 <div class="show">{{text}}</div> 3 <input type="button" value="弹出" ng-click="onShow('我是一个弹出对话框')"> 4 <input type="button" value="显示" ng-click="onWrite('今天天气有点冷啊')"> 5 </div>
1 var app = angular.module('myApp', []); 2 app.factory('$show', ['$window', function ($window) { 3 return { 4 show: function (text) { 5 $window.alert(text); 6 } 7 } 8 }]); 9 app.factory('$write', function () { 10 return { 11 write: function (text) { 12 return text; 13 } 14 } 15 }); 16 17 var myCtrl = function ($scope, $show, $write) { 18 $scope.onShow = function (msg) { 19 $show.show(msg); 20 } 21 $scope.onWrite = function (msg) { 22 $scope.text = $write.write(msg); 23 } 24 }; 25 app.controller('myCtrl', myCtrl); 26 myCtrl.$inject = ['$scope', '$show', '$write'];
这种注入方式由于服务名和函数参数名在名称和顺序的一一对应关系,使得服务名和函数体绑定在一起,因此这种方式可以在压缩或混淆后的代码中执行。
3.行内式注入
所谓行内式注入,就是在构建一个Angular对象时,允许将一个字符型数组作为对象的参数,而不仅仅是一个函数。
在这个数组中,除最后一个必须是函数体外,其余都代表注入对象中的服务名,而他们的名称和顺序与最后一个函数的参数是一一对应的。
示例:
1 <div ng-controller="myCtrl"> 2 <div class="show">{{text}}</div> 3 <input type="button" value="求和" ng-click="onClick(5,10)"> 4 </div>
1 var app = angular.module('myApp', []); 2 app.factory('$sum', function () { 3 return { 4 add: function (m, n) { 5 return m + n; 6 } 7 } 8 }); 9 app.controller('myCtrl', ['$scope', '$sum', function ($scope, $sum) { 10 $scope.onClick = function (m, n) { 11 $scope.text = m + " + " + n + ' = ' + $sum.add(m, n); 12 } 13 }]);
这种方法更简洁,同样也能在压缩或混淆后的代码中执行。
四、$injector常用API
Angular中依赖注入离不开一个重要的对象--注入器($injector
),整个Angular应用中的注入对象都由它负责定位和创建,它也有很多方法,如get
、has
、invoke
等。
1.has和get方法
has
方法的功能是根据传入的名称,从注册的列表中查找对应的服务,如果找到返回true,否则返回false。
1 injector.has(name) 2 // injector为获取的$injector对象 3 // name为需要查找的服务名称
get
方法返回指定名称的服务实例,获取到服务的实例对象后,就可以直接调用服务中的属性和方法。
1 injector.get(name) 2 // injector为获取的$injector对象 3 // name为需要返回实例的服务名称
示例:
1 var app = angular.module('myApp', []); 2 app.factory('$custom', function () { 3 return { 4 print: function (msg) { 5 console.log(msg); 6 } 7 } 8 }); 9 // 获取injector对象 10 var injector = angular.injector(['app', 'ng']); 11 // 通过has方法判断是否有$custom服务 12 var has = injector.has('$custom'); 13 console.log(has); 14 // 判断如果存在$custom服务,则调用其方法在控制台输出任意字符 15 if (has) { 16 var custom = injector.get('$custom'); 17 custom.print('控制台输入任意的内容!'); 18 } 19 app.controller('myCtrl', ['$scope', '$custom', function ($scope, $custom) { 20 // .... 21 }]);
2.invoke方法
invoke
方法可以执行一个自定义的函数,除此之外,在执行函数时,还能传递变量给函数自身。
1 injector.invoke(fn, [self], [locals]) 2 // injector为获取的$injector对象 3 // fn为需要执行的函数名称 4 // self是可选参数,它是一个对象,表示用于函数中的this变量 5 // locals可选参数,也是一个对象,它能为函数中的变量名的传递提供方法支持
示例:
1 var myapp = angular.module('myApp', []); 2 myapp.factory('$custom', function () { 3 return { 4 print: function (msg) { 5 console.log(msg); 6 } 7 } 8 }); 9 var injector = angular.injector(['myapp', 'ng']); 10 var fun = function ($custom) { 11 $custom.print('函数执行成功!'); 12 } 13 injector.invoke(fun); 14 myapp.controller('myCtrl', ['$scope', '$custom', function ($scope, $custom) { 15 //... 16 }]);