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操作符就行. 能够通过全局变量查找依赖. 依赖能在需要时被导入. 前 ...
随机推荐
- mini2440的SDRAM分析
首先是2440的存储控制器: 暂时不管是从nand启动还是nor启动,因为我现在只关注内存,从上图可以看到由2440的Memory Controller可以寻址的范围是0x0000,0000---0x ...
- [麦先生]如何使用AJAX实现按需加载
按需加载的优势:在实际调查中发现,很多的网民在游览网站时具有明确的指向性,往往在进入主页后直接搜索进入自己需要的商品列表内,如果在客户进入主页时将主页信息全部加载完毕后展示给顾客,会极大的浪费网站资源 ...
- 快速熟悉Zbrush中的四种裁切笔刷
ZBrush 4.0R4完善了裁切的功能.捷就可以把模型想要隐藏的地方用选择裁切的方法隐藏掉,其中ZBrush在®4.0R4软件中提供了4中裁切笔刷,分别为ClipCircle笔刷.ClipCircl ...
- 我的第一篇博客--SQL小语句
开通了博客,拥有了属于自己的小小天地.先写一篇今儿刚学到的 1 remove mirroring relationship alter database datab_name set partner ...
- HDU 3600 Simple Puzzle 归并排序 N*N数码问题
先介绍八数码问题: 我们首先从经典的八数码问题入手,即对于八数码问题的任意一个排列是否有解?有解的条件是什么? 我在网上搜了半天,找到一个十分简洁的结论.八数码问题原始状态如下: 1 2 3 4 5 ...
- flex+AS3编程规范
flex+AS3编程规范 Flex+AS3编码规范 http://www.cnblogs.com/jiahuafu/ 1. 缩写: 尽量避免使用缩写,使用缩写时尽量和Flex保持一致.但要记住一 ...
- main方法的理解
1),在执行一个类的时候,所找到的方法是mian(). 2)string args[]:输入的参数. public class StaticDemo08{ public static void mai ...
- 022医疗项目-模块二:药品目录的导入导出-对XSSF导出excel类进行封装
资源全部来源于传智播客. 好的架构师写的程序,就算给刚入门的新手看,新手一看就知道怎么去用.所以我们要对XSSF导出excel类进行封装.这是架构师的工作,但我们也要知道. 我们写一个封装类: 这个类 ...
- 07SpringMvc_jsp到jsp的控制器_ParameterizableViewController
本文主要讲的是控制器,Action继承什么类.记得Springmvc系列的第一篇文章说过.SpirngMVC的实现流程.
- android Camera 录像时旋转角度
录像保存时,旋转角度要与所拍录像时的角度保持一致,否则,看起来就会出现角度不度,巅倒等问题. 一般在开始录像之前会先去初始化录像 initializeRecorder 中会去读取当前的录像或拍照的旋转 ...