依赖注入

依赖注入(DI)是一种软件设计模式,处理组件如何获取其依赖关系。

AngularJS注入器子系统负责创建组件,解析它们的依赖关系,并根据请求将它们提供给其他组件。

使用依赖注入

DI遍布AngularJS。 可以在定义组件或为模块提供运行和配置块时使用它。

  • 诸如服务,指令,过滤器和动画之类的组件由注入工厂方法或构造函数定义。 这些组件可以注入“服务”和“值”组件作为依赖关系。
  • 控制器由构造函数定义,可以使用任何“服务”和“值”组件作为依赖关系注入,但是它们也可以提供特殊的依赖关系。
  • run方法接受一个函数,它可以注入"service","value"和"constant"组件作为依赖。 请注意,不能将"providers"注入运行块。
  • config方法接受一个函数,它可以注入"provider"和"constant"组件作为依赖。 请注意,无法将“服务”或“值”组件注入配置。

工厂方法

定义指令,服务或过滤器的方式是使用工厂函数。 工厂方法使用模块注册。 推荐的工厂声明方式是:

angular.module('myModule', [])
.factory('serviceId', ['depService', function(depService) {
// ...
}])
.directive('directiveName', ['depService', function(depService) {
// ...
}])
.filter('filterName', ['depService', function(depService) {
// ...
}]);

模块方法

我们可以通过调用config和run方法指定要在模块的配置和运行时运行的函数。 这些函数是可注入的依赖关系,就像上面的工厂函数。

angular.module('myModule', [])
.config(['depProvider', function(depProvider) {
// ...
}])
.run(['depService', function(depService) {
// ...
}]);

控制器

控制器是“类”或“构造函数”,它们负责提供支持模板中的声明性标记的应用程序行为。 推荐的声明控制器的方法是使用数组符号:

someModule.controller('MyController', ['$scope', 'dep1', 'dep2', function($scope, dep1, dep2) {
...
$scope.aMethod = function() {
...
}
...
}]);

与服务不同,应用程序中可能有许多相同类型的控制器实例。

此外,对控制器提供附加的依赖性:

  • $scope:控制器与DOM中的元素相关联,因此提供对该作用域的访问。 其他组件(如服务)只能访问$rootScope服务。
  • resolves:如果将控制器实例化为路由的一部分,则作为路由的一部分解析的任何值都可用于注入到控制器中。

依赖注解

AngularJS通过注入器调用某些功能(如服务工厂和控制器)。 需要注解这些函数,以便注入器知道要注入到函数中的服务。 有三种方法用服务名称信息注解代码:

  • 使用内联数组注解(首选)
  • 使用$inject属性注解
  • 隐含地从函数参数名称(有警告)

内联数组注解

这是注解应用程序组件的首选方式。 这是文档中的示例如何编写。

例如:

someModule.controller('MyController', ['$scope', 'greeter', function($scope, greeter) {
// ...
}]);

这里我们传递一个数组,其元素由一个字符串列表(依赖项的名称)和函数本身组成。

当使用这种类型的注解时,注意保持注解数组与函数声明中的参数同步。

$inject属性注解

为了允许简写重命名函数参数并仍然能够注入正确的服务,该函数需要使用$inject属性注解。 $inject属性是要注入的服务名称的数组。

var MyController = function($scope, greeter) {
// ...
}
MyController.$inject = ['$scope', 'greeter'];
someModule.controller('MyController', MyController);

在这种情况下,$inject数组中的值的顺序必须与MyController中的参数的顺序匹配。

就像数组注解一样,你需要注意保持$inject与函数声明中的参数同步。

隐式注解

注意:如果计划缩小代码,你的服务名称将被重命名并破坏你的应用程序。

someModule.controller('MyController', function($scope, greeter) {
// ...
});

给定一个函数,注入器可以通过检查函数声明并提取参数名称来推断要注入的服务的名称。 在上面的例子中,$scope和greeter是需要注入到函数中的两个服务。

这种方法的一个优点是没有与函数参数保持同步的名称数组。 可以自由地重新排序依赖项。

但是这个方法不能使用JavaScript minifiers / obfuscators,因为他们会重命名参数。

像ng-annotate这样的工具可以在应用程序中使用隐式依赖关系注解,并在缩小之前自动添加内联数组注解。 如果你决定采取这种方法,你可能想使用ng-strict-di。

由于这些注意事项,我们建议避免使用此样式的注解。

使用严格依赖注入

可以在与ng-app相同的元素上添加ng-strict-di指令以选择进入严格DI模式:

<!doctype html>
<html ng-app="myApp" ng-strict-di>
<body>
I can add: {{ 1 + 2 }}.
<script src="angular.js"></script>
</body>
</html>

只要服务尝试使用隐式注解,严格模式就会抛出错误。

考虑这个模块,其中包括一个使用隐式DI的willBreak服务:

angular.module('myApp', [])
.factory('willBreak', function($rootScope) {
// $rootScope是被隐式注解
})
.run(['willBreak', function(willBreak) {
//当它运行时AngularJS抛出这个异常
}]);

当willBreak服务被实例化时,AngularJS将抛出一个错误,因为严格的模式。 当使用ng-annotate等工具确保所有应用程序组件都有注解时,这是非常有用的。

如果使用手动引导,还可以通过在可选配置参数中提供strictDi:true来使用严格的DI:

angular.bootstrap(document, ['myApp'], {
strictDi: true
});

为什么要依赖注入?

这部分激励和解释AngularJS使用DI。有关如何使用DI,请参见上文。

组件(对象或函数)只有三种方式可以获得它的依赖:

  • 组件可以创建依赖关系,通常使用new运算符。
  • 组件可以通过引用全局变量来查找依赖关系。
  • 组件可以在需要的地方传递依赖。

创建或查找依赖关系的前两个选项不是最优的,因为它们会对组件的依赖性进行硬编码。这使得很难,即使不是不可能,修改依赖性。这在测试中尤其成问题,其中通常期望提供用于测试隔离的模拟依赖性。

第三个选项是最可行的,因为它消除了从组件定位依赖性的责任。依赖项简单地交给组件。

function SomeClass(greeter) {
this.greeter = greeter;
} SomeClass.prototype.doSomething = function(name) {
this.greeter.greet(name);
}

在上面的例子中,SomeClass不关心创建或定位greeter依赖,它只是在实例化时传递给greeter。

这是可取的,但它负责获取构造SomeClass的代码的依赖。

为了管理依赖项创建的责任,每个AngularJS应用程序都有一个注入器。 注入器是负责构建和查找依赖性的服务定位器。

以下是使用注入器服务的示例:

// 在模块中提供连接信息
var myModule = angular.module('myModule', []);

指示注入器如何建立一个greeter服务。 注意greeter依赖于$window服务。 greeter服务是一个包含greet方法的对象。

myModule.factory('greeter', function($window) {
return {
greet: function(text) {
$window.alert(text);
}
};
});

创建一个新的注入器,它可以提供myModule模块中定义的组件,并从注入器请求我们的greeter服务。 (这通常由AngularJS引导自动完成)。

var injector = angular.injector(['ng', 'myModule']);
var greeter = injector.get('greeter');

请求依赖性解决了硬编码的问题,但它也意味着注入器需要在整个应用程序中传递。 通过注入器打破了Demeter的定律。 为了弥补这一点,我们在HTML模板中使用声明符号,将创建组件的责任交给注入器,如下例所示:

<div ng-controller="MyController">
<button ng-click="sayHello()">Hello</button>
</div>
function MyController($scope, greeter) {
$scope.sayHello = function() {
greeter.greet('Hello World');
};
}

当AngularJS编译HTML时,它会处理ng-controller指令,然后请求注入器创建控制器及其依赖关系的实例。

injector.instantiate(MyController);

这一切都在幕后完成。 注意,通过让ng-controller要求注入器实例化类,它可以满足MyController的所有依赖性,而控制器不知道注入器。

这是最好的结果。 应用程序代码简单地声明它需要的依赖性,而不必处理注入器。 这个设置不打破德米特法。

Angular开发者指南(七)依赖注入的更多相关文章

  1. Angular开发者指南(二)概念概述

    template(模板):带有附加标记的模板HTML directives(指令):使用自定义属性和元素扩展HTML model(模型):用户在视图中显示的数据,并与用户进行交互 scope(作用域) ...

  2. Angular JS中的依赖注入

    依赖注入DI angularjs中与DI相关有angular.module().angular.injector(). $injector.$provide. DI 容器3要素:服务的注册.依赖关系的 ...

  3. Angular开发者指南(一)入门介绍

    什么是Angular AngularJS是动态Web应用程序的结构框架. 它允许您使用HTML作为模板语言,并允许您扩展HTML的语法以清晰,简洁地表达应用程序的组件.AngularJS的数据绑定和依 ...

  4. Angular 行内式依赖注入

    var app = angular.module('myApp', ['ng']); //创建一个自定义服务app.factory('$Debug', function () { return { d ...

  5. Angular : IOC的方式:依赖注入

    依赖注入 @Component, @Injectable 可以允许别的声明在providers里面的Service等注入到被这两个装饰器装饰的类中 Service等可以被声明在app-module.t ...

  6. Angular开发者指南(五)服务

    服务 AngularJS服务是使用依赖注入(DI)连接在一起的可替代对象. 可以使用服务在整个应用程式中整理和分享程式码. AngularJS服务有: 延迟初始化 - AngularJS只在应用程序组 ...

  7. Angular开发者指南(四)控制器

    了解控制器controller 在AngularJS中,Controller由JavaScript构造函数定义,用于扩充AngularJS Scope. 当控制器通过ng-controller指令连接 ...

  8. 极简SpringBoot指南-Chapter02-Spring依赖注入的方式

    仓库地址 w4ngzhen/springboot-simple-guide: This is a project that guides SpringBoot users to get started ...

  9. Vue实战指南之依赖注入(provide / inject)

    案例 UI美眉说咱家的选项菜单太丑了,小哥哥能不能美化一下呀,洒家自然是说小意思啦~自定义一个select组件,so easy~ 简单粗暴型: <el-select v-model=" ...

随机推荐

  1. 白痴级教程,新手看过来,具详细实操文档 (word图片复制不过来,0202年了还有这样的不便利,下回研究一下,图片下次补)

    一.环境配置(win10): 1.配置cmd的python环境为arcmap10.2 自带的python解释器(2.7.3)(自带arcpy库) 具体操作: 1我的电脑右击属性,打开 (选中path点 ...

  2. 用Axure画原型图有感

    感觉前端做UE非常有优势啊- 但是在制作的时候,似乎陷入了误区: (1)只求原型图的漂亮,色彩丰富,忽略了其本质作用,是用来整理逻辑,画出逻辑流程的. (2)一开始就追求交互,高保真的原型,忽视了细节 ...

  3. JavaScript—封装animte动画函数

    封装Animte 动画函数 虽然可能以后的开发中可能根本不需要自己写,Jquery 给我们封装好了,或者用CSS3的一些属性达到这样的效果可能更简单. 我比较喜欢底层的算法实现,万变不离其中,这个逻辑 ...

  4. Django2.0——Form组件简单总结

    Django提供了一个Form组件来配和前端的表单进行使用,Form有两个强大的功能,分别是生成HTML代码和验证数据的合法性.通常我们不会用其第一个功能,因为前端的设计可以做出更加精美且多样的表单页 ...

  5. Eureka高可用环境搭建

    1.创建govern-center 子工程 包结构:com.dehigher.govern.center 2.pom文件 (1)父工程pom,用于依赖版本管理 <dependencyManage ...

  6. LINUX的ssh免密码配置

    本篇将介绍LINUX的ssh免密码配置. 本篇中的有三台主机: 192.168.1.110 master.com.cn  主机192.168.1.111 salver1.com.cn192.168.1 ...

  7. java实现图片文件与Base64的互转

    通过form表单上传图片时,有时候web容器对文件大小的限制会影响我们上传.这时,前端页面可以考虑将图片转换成base64串来实现上传. 图片与Base64的互转,其实就是利用了文件流与Base64的 ...

  8. 第04项目:淘淘商城(SpringMVC+Spring+Mybatis) 的学习实践总结【第一天】

    本人做过一年的MATLAB编程和简单维护过VB和C++的项目.是跟着网上获得的黑马的Java双元视频课来自学入门Java知识和常用框架的使用. 淘淘商城(SpringMVC+Spring+Mybati ...

  9. cin cout

    编写一个程序,要求用户输入一串整数和任意数目的空格,这些整数必须位于同一行中,但允许出现在该行的任何位置.当用户按下enter键,数据输入停止.程序自动对所有的整数进行求和并打印出结果. 需要解决两个 ...

  10. mysql 获取数据库和表结构信息

    SELECT * FROM information_schema.`TABLES` where TABLE_SCHEMA = '数据库名';SELECT * FROM information_sche ...