AngularJS开发指南15:AngularJS的创建服务,将服务注入到控制器,管理服务依赖详解
创建服务
虽然AngularJS提供了很多有用的服务,但是如果你要创建一个很棒的应用,你可能还是要写自己的服务。你可以通过在模块中注册一个服务工厂函数,或者通过Module#factory api或者直接通过模块配置函数中的$provide api来实现。
所有的服务都符合依赖注入的原则。它们用一个唯一的名字将自己注册进AngularJS的依赖注入系统(injector),并且声明需要提供给工厂函数的依赖。它们的依赖在测试中可以是虚拟的,这使得它们能很好地被测试。
注册服务
要注册服务,你首先要有一个包含该服务的模块。然后你就能通过模块的api或者使用模块配置函数中的$provide服务来注册你的服务了。下面的伪代码显示了这两种方法。
使用angular.Module api:
var myModule = angular.module('myModule', []);
myModule.factory('serviceId', function() {
var shinyNewServiceInstance;
//factory function body that constructs shinyNewServiceInstance
return shinyNewServiceInstance;
});
使用$provide服务:
angular.module('myModule', [], function($provide) {
$provide.factory('serviceId', function() {
var shinyNewServiceInstance;
//factory function body that constructs shinyNewServiceInstance
return shinyNewServiceInstance;
});
});
注意,你不应该注册一个服务实例,而是一个会在被调用时创建实例的工厂函数。
依赖
服务不仅可以被依赖,还可以有自己的依赖。依赖可以在工厂函数的参数中指定。参阅AngularJS的依赖注入系统,和使用依赖的数组表示法和$inject属性来让依赖表示精简化。
下面是一个很简单的服务的例子。这个服务依赖于$window服务(会被当成参数传递给工厂函数),并且只是个函数。这个服务的任务是存储所有的通知;在第三个通知以后,服务会用window的alert来输出所有的通知。
angular.module('myModule', [], function($provide) {
$provide.factory('notify', ['$window', function(win) {
var msgs = [];
return function(msg) {
msgs.push(msg);
if (msgs.length == 3) {
win.alert(msgs.join("\n"));
msgs = [];
}
};
}]);
});
实例化AngularJS的服务
所有服务都是延迟实例化的。这意味着所有的服务只有在需要时,或者被依赖时才会实例化。换句话说,AngularJS不会实例化服务,除非被请求了或者被应用直接或间接依赖了。
要注意的是所有的AngularJS服务都是单例的。这意味着在每一个注入器中都只有一个需要的服务的实例。
将服务注入到控制器中
将服务用作控制器的依赖和将服务用作其他服务的依赖很类似。
显式依赖注入
因为Javascript是一种动态语言,依赖注入系统无法通过静态类型来知道应该注入什么样的服务(静态类型语言就可以)。所以,你应该用$inject的属性来指定服务的名字,这个属性是一个包含需要注入的服务的名字字符串的数组。名字要和服务注册到系统时的名字匹配。服务的名称的顺序也很重要:当执行工厂函数时传递的参数是依照数组里的顺序的。但是工厂函数中参数的名字不重要,但最好还是和服务本身的名字一样。举个例子:
angular.
module('MyServiceModule', []).
factory('notify', ['$window', function(win) { //定义了一个服务notify
var msgs = [];
return function(msg) {
msgs.push(msg);
if (msgs.length == 3) {
win.alert(msgs.join("\n"));
msgs = [];
}
};
}]); function myController(scope, notifyService) { //这里的scope是$scope,notifyService是notify服务
scope.callNotify = function(msg) {
notifyService(msg);
};
} myController.$inject = ['$scope','notify']; //显式定义依赖注入
隐式依赖注入
AngularJS依赖注入系统的新特性使得AngularJS可以通过参数名称来判断依赖。下面的例子展示一下隐式地依赖$window, $scope:
angular.
module('MyServiceModuleDI', []).
factory('notify', function($window) {
var msgs = [];
return function(msg) {
msgs.push(msg);
if (msgs.length == 3) {
$window.alert(msgs.join("\n"));
msgs = [];
}
};
}); function myController($scope, notify) { //通过参数名字,来隐式的定义依赖注入
$scope.callNotify = function(msg) {
notify(msg);
};
但是如果你要压缩你的代码,你的变量名会被重命名,所以这时你就只能显示地指定依赖了。
管理服务依赖
要声明服务的依赖,你可以在工厂方法参数中隐式指明他们,也可以将$inject属性设置成包含了依赖名称的数组,或者是使用数组表示法。不推荐使用$inject属性的这种方法。
使用数组表示法:
function myModuleCfgFn($provide) {
$provide.factory('myService', ['dep1', 'dep2', function(dep1, dep2) {}]); //依赖dep1,dep2服务
}
使用$inject属性:
function myModuleCfgFn($provide) {
var myServiceFactory = function(dep1, dep2) {};
myServiceFactory.$inject = ['dep1', 'dep2']; //依赖dep1,dep2服务
$provide.factory('myService', myServiceFactory);
}
使用隐式依赖(使用代码压缩时会失效):
function myModuleCfgFn($provide) {
$provide.factory('myService', function(dep1, dep2) {});
}
举个例子来说明下服务之间的依赖:
function batchLogModule($provide){
$provide.factory('batchLog', ['$timeout', '$log', function($timeout, $log) {
var messageQueue = [];
function log() {
if (messageQueue.length) {
$log('batchLog messages: ', messageQueue);
messageQueue = [];
}
$timeout(log, 50000);
}
log();
return function(message) {
messageQueue.push(message);
}
}]);
$provide.factory('routeTemplateMonitor',
['$route', 'batchLog', '$rootScope',
function($route, batchLog, $rootScope) {
$rootScope.$on('$routeChangeSuccess', function() {
batchLog($route.current ? $route.current.template : null);
});
}
]
);
}
angular.injector([batchLogModule]).get('routeTemplateMonitor');
上例中有几点要注意:
- batchLog服务依赖内建的$timeout和$log服务,并且允许消息批量地使用console.log记录。
- 和batchLog服务一样,routeTemplateMonitor服务依赖内建的$route服务。
- 自定义的服务都使用隐式表示和数组法来表示自己的依赖。最重要的是数组中的服务的名字顺序要和工厂函数参数的名字顺序对应。除非依赖是隐式地通过函数参数名表示的,那么就是有声明依赖的数组名称顺序决定依赖注入的顺序。
总结
AngularJS服务是一种能执行一个常见操作的单例,比如$http服务是用来操作浏览器的XMLHttpRequest对象的。
要使用AngularJS服务,你只需要在需要的地方(控制器,或其他服务)指出依赖就行了。AngularJS的依赖注入系统会帮你完成剩下的事情。它负责实例化,查找左右依赖,并且按照工厂函数要求的样子传递依赖。
AngularJS web框架提供了一组常用操作的服务。和其他的内建变量或者标识符一样,内建服务名称也总是以"$"开头。另外你也可以创建你自己的服务。
加油!
AngularJS开发指南15:AngularJS的创建服务,将服务注入到控制器,管理服务依赖详解的更多相关文章
- AngularJS开发指南3:Angular主要组成部分以及如何协同工作
AngularJS的主要组成部分是: 启动(startup) - 展示“hello world!” 执行期(runtime) - AngularJS 执行期概览 作用域(scope) - 视图和控制器 ...
- Qt开发技术:QCharts(三)QCharts样条曲线图介绍、Demo以及代码详解
若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...
- Linux服务器,服务管理--systemctl命令详解,设置开机自启动
Linux服务器,服务管理--systemctl命令详解,设置开机自启动 syetemclt就是service和chkconfig这两个命令的整合,在CentOS 7就开始被使用了. 摘要: syst ...
- Linux服务管理 systemctl命令详解
Linux服务器,服务管理--systemctl命令详解,设置开机自启动 syetemclt就是service和chkconfig这两个命令的整合 任务 旧指令 新指令 使某服务自动启动 ch ...
- AngularJS开发指南14:AngularJS的服务详解
服务是一种由服务器端带到客户端的特性,它由来已久.AngularJS应用中的服务是一些用依赖注入捆绑在一起的可替换的对象.服务是最常和依赖注入一起用的,它也是AngularJS中的关键特性. 接下来, ...
- AngularJS开发指南16:AngularJS构建大型Web应用详解
AngularJS是由Google创建的一种JS框架,使用它可以扩展应用程序中的HTML功能,从而在web应用程序中使用HTML声明动态内容.在该团队工作的软件工程师Brian Ford近日撰写了一篇 ...
- AngularJS开发指南8:AngularJS模块的详解
在讲angularjs的模块之前,我们先介绍一下angular的一些知识点: AngularJS是纯客户端技术,完全用Javascript编写的.它使用的是网页开发的常规技术(HTML,CSS,Jav ...
- AngularJS开发指南11:AngularJS的model,controller,view详解
model model这个词在AngularJS中,既可以表示一个(比如,一个叫做phones的model,它的值是一个包含多个phone的数组)对象,也可以表示应用中的整个数据模型,这取决于我们所讨 ...
- AngularJS开发指南10:AngularJS依赖注入的详解
依赖注入是一种软件设计模式,用来处理代码的依赖关系. 一般来说有三种方法让函数获得它需要的依赖: 它的依赖是能被创建的,一般用new操作符就行. 能够通过全局变量查找依赖. 依赖能在需要时被导入. 前 ...
随机推荐
- 虚拟机Linux----Ubuntu1204----安装jdk1.8
1.介绍 这里主要讲一下,如何在Ubuntu1204下通过压缩包的方式安装jdk1.8,rpm的直接运行就行了. 2.步骤 2.1 下载 地址:http://www.oracle.com/techne ...
- UVALive - 2965 Jurassic Remains (LA)
Jurassic Remains Time Limit: 18000MS Memory Limit: Unknown 64bit IO Format: %lld & %llu [Sub ...
- Oracle Jdbc demo
两种方式: thin是一种瘦客户端的连接方式,即采用这种连接方式不需要安装oracle客户端,只要求classpath中包含jdbc驱动的jar包就行.thin就是纯粹用Java写的ORACLE数据库 ...
- Spring中AOP原理,源码学习笔记
一.AOP(面向切面编程):通过预编译和运行期动态代理的方式在不改变代码的情况下给程序动态的添加一些功能.利用AOP可以对应用程序的各个部分进行隔离,在Spring中AOP主要用来分离业务逻辑和系统级 ...
- css中元素居中总结
很多时候,我们需要让元素居中显示:1. 一段文本的水平居中,2. 一张图片的水平居中,3. 一个块级元素的水平居中:4. 单行文本的竖直居中,5. 不确定高度的一段文本竖直居中,6. 确定高度的块级元 ...
- QEMU启动时插入tap虚拟网卡
1.利用brctl命令创建虚拟网桥br0 brctl addbr br0 ifconfig br0 up //上述两条命令分开执行会导致网络断开 2.将虚拟网桥br0与物理网卡eth0绑定 brctl ...
- 【读书笔记《Android游戏编程之从零开始》】7.Android 游戏开发常用的系统控件(Dialog)
在Android应用开发中,Dialog(对话框)创建简单且易于管理因而经常用到,对话框默认样式类似创建样式的Activity.首先介绍android.app.AlertDialog下的Builder ...
- mac os利用xampp实现apache下的cgi
折腾了两天终于把问题解决了,mac os是10.10.3..够新了吧 系统原生的apache配置cgi老是配不好,突然发现xampp也有macos版的,准备下下来试试. 安装完成后,打开apache服 ...
- as3 Point
首先我们看看Point类的属性:x:Number——该点的水平坐标y:Number——该点的垂直坐标length:Number——从(0,0)到此点的线段长度(只读属性)从length属性我们可以看到 ...
- android源码在线查看
http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android/