在这之前angular学习笔记(十五)-module里的'服务'这篇文章里,已经大致讲解了ng中的'服务',在之后的很多地方也用到了服务,但是,所有的服务都是使用app.factory来创建的.但其实,创建服务有5种方法,这篇文章就来具体讲解ng中的五种服务类型.

首先,为了举栗子,先写好如下的模型,控制器,html:

html:

<!DOCTYPE html>
<html ng-app="serviceApp">
<head>
<title>服务</title>
<meta charset="utf-8">
<script src="../angular.js"></script>
<script src="script.js"></script>
<!-- <script src="script_2.js"></script>-->
</head>
<body >
<div ng-controller="myCtrl">
name:{{name}}
<br/>
age:{{age}}
<br/>
love:{{love}}
<br/>
money:{{money}}
<br/>
id:{{id}}
</div>
<hr/>
<div ng-controller="myOtherCtrl">
name:{{name}}
<br/>
love:{{love}}
<br/>
</div>
<hr/>
</body>
</html>

js:

var serviceApp = angular.module('serviceApp',[]);

serviceApp.controller('myCtrl',function($scope,myConfig){
$scope.name = myConfig.name;
$scope.love = myConfig.love;
$scope.age = myConfig.age;
$scope.money = myConfig.money;
$scope.id = myConfig.getId();
$scope.$watch(myConfig.love,function(){$scope.love = myConfig.love;});
})
serviceApp.controller('myOtherCtrl',function($scope,myConfig){
$scope.name = myConfig.name;
$scope.love = myConfig.love;
angular.extend(myConfig,{love:'zxg'});
$scope.$watch(myConfig.love,function(){$scope.love = myConfig.love;});
});

如上,serviceApp模型里有两个控制器,myCtrl和myOtherCtrl,这两个控制器都注入了myConfig这个服务.我们在后面就通过创建不同的myConfig服务来查看结果

一.constant服务:

app.constant('name',obj)

name为服务的名字,obj为一个json对象.

js:

serviceApp.constant('myConfig',{
name:'code_bunny',
age:12,
getId:function(){
return 1
}
});

运行结果: http://jsfiddle.net/f9qq0t50/1/

说明:

constant创建服务返回一个json对象(也就是第二个参数中传入的对象),这个对象里可以有参数,可以有方法,并且,属性和方法都可以在控制器中修改,新增,但是按照它的设计本意,一般constant创建的服务不会去修改它的内容,需要修改内容,最好用value来创建服务.

注意点:

1.它是一个引用对象,无论被注入多少个控制器中,实际都指向同一个对象,所以,无论修改其中的哪一个,其它所有的服务都会被改变.

2.服务修改过后,ng不会自动同步,简单的说,就是它没有做到自动双向绑定数据,比如在这里给服务新增了love属性:angular.extend(myConfig,{love:'zxg'}),我需要使用$scope.$watch(myConfig.love,function(){$scope.love = myConfig.love;})这样才能把love属性值同步到视图中.

3.constant服务不能通过decorator进行装饰,(什么是装饰下面会讲到)

二.value服务:

app.value('name',obj)

name为服务的名字,obj为一个json对象.

js:

serviceApp.value('myConfig',{
name:'code_bunny',
age:12,
getId:function(){
return 1
}
});
serviceApp.config(function($provide){
$provide.decorator('myConfig',function($delegate){
$delegate.money = '100w';
return $delegate
})
});

运行结果: http://jsfiddle.net/p0dqr7wy/1/

说明:

value创建服务返回一个json对象(也就是第二个参数中传入的对象),这个对象里可以有参数,可以有方法,并且,属性和方法都可以在控制器中修改,新增,按照它的设计本意,如果属性和方法需要被修改内容,就用value来创建服务.

constant和value主要就是用于存放一些数据或方法以供使用,区别是constant一般是存放固定内容,value存放可能会被修改的内容

注意点:

1.同constant注意点1

2.同constant注意点2

3.value可以被装饰,所以这里myConfig服务拥有了money属性.(装饰具体怎么用,下面会说)

三.factory服务

app.factory('name',function(){return obj})

name为服务的名字,第二个参数传入一个函数,函数需要有一个返回值obj,返回一个对象.实际被注入的服务就是这个对象.

js:

serviceApp.factory('myConfig',function(){
var myname = 'code_bunny';
var age = 12;
var id = 1;
return {
name: myname,
age: age,
getId: function(){
return id
}
}
});

或者是这样:

serviceApp.factory('myConfig',function(){
return new constructorFun()
}); function constructorFun(){
var myname = 'code_bunny';
var age = 12;
var id = 1;
this.name = myname;
this.age = age;
this.getId = function(){
return id
}
}

装饰部分代码:

serviceApp.config(function($provide){
$provide.decorator('myConfig',function($delegate){
console.log($delegate);
$delegate.money = '100w';
return $delegate
})
});

运行结果:

http://jsfiddle.net/8kuxt3xc/

http://jsfiddle.net/ua2y617q/

说明:

factory服务是最常见最常用的服务类型,几乎可以满足90%的自己开发的需求,使用它可以编写一些逻辑,通过这些逻辑最后返回所需要的对象.比如使用$http来获取一些数据.我们就在factory创建的服务里抓取数据,最后返回.

它和constant,value最大的区别是,factory服务是有一个处理过程,经过这个过程,才返回结果的.

注意点:

1.同constant注意点1

2.同constant注意点2

3.factory返回的服务也可以被装饰,所以这里myConfig服务拥有了money属性.(装饰具体怎么用,下面会说)

四.service服务

app.service('name',constructor)

name为服务的名字,constructor是一个构造函数.

js:

serviceApp.service('myConfig',function(){
var myname = 'code_bunny';
var age = 12;
var id = 1;
this.name = myname;
this.age = age;
this.getId = function(){
return id
}
});

或者是这样:

serviceApp.service('myConfig',constructorFun);
function constructorFun(){
var myname = 'code_bunny';
var age = 12;
var id = 1;
this.name = myname;
this.age = age;
this.getId = function(){
return id
}
}

装饰部分代码同上.

运行结果:

http://jsfiddle.net/1qj8m5ot/

http://jsfiddle.net/0bh67cog/

说明:

service和factory的区别在于,它第二个参数传入的是一个构造函数,最后被注入的服务是这个构造函数实例化以后的结果.所以基本上使用service创建的服务的,也都可以使用factory来创建.

所以这里,factory服务的第二种写法和使用service是一致的:

serviceApp.factory('myConfig',function(){
return new constructorFun()
});
//等价于
serviceApp.service('myConfig',constructorFun);

注意点:

1.同constant注意点1

2.同constant注意点2

3.service返回的服务也可以被装饰,所以这里myConfig服务拥有了money属性.(装饰具体怎么用,下面会说)

五.provider服务

app.provider('name',function(){
  ....
  return {
    ...
    $get:function(){
      ...
      return obj
    }
}
})

name为服务的名字,第二个参数接受一个函数,函数返回一个对象,返回的对象比如要有$get方法,$get方法必须要返回一个对象obj,这个对象就是真正被注入的服务.

栗子一:

js:

serviceApp.provider('myConfig',function(){
return {
$get:function(){
var myname = 'code_bunny';
var age = 12;
var id = 1;
return {
name: myname,
age: age,
getId: function(){
return id
}
}
}
}
});

装饰部分代码同上.

运行结果: http://jsfiddle.net/2pz2ft73/

说明:

provider服务的第二个参数的返回值中必须要有$get方法(除了$get,还可以有其它方法,后面的例子会说到),$get方法就相当于factory服务的第二个参数,最后要返回一个对象,这个对象就是真正被注入的服务:

栗子二:

js:

serviceApp.provider('myConfig',function(){
var id = 1;
return {
setID:function(newID){
id = newID
},
$get:function(){
var myname = 'code_bunny';
var age = 12;
return {
name: myname,
age: age,
getId: function(){
return id
}
}
}
}
});
serviceApp.config(function(myConfigProvider){
myConfigProvider.setID(2)
});

装饰部分代码同上.

运行结果:http://jsfiddle.net/hcpemex3/

说明:

这里的provider服务不仅仅返回了$get方法,还返回了setID方法,然后id变量是写在函数里的,返回值的外面,形成一个闭包,可以被修改.

然后,在provider服务里定义的方法,可以在config函数里调用.注意调用的格式:

serviceApp.config(function(myConfigProvider){
myConfigProvider.setID(2)
});

被注入的服务名不叫myConfig,而是myConfigProvider.然后在函数里面可以调用myConfigProvider的setID方法(也就是myConfig的setID方法).

通过这种方式,使得我们的服务可以被手动配置,比如这里可以配置id.

ng有很多内置的服务都有这样的功能,比如$route服务,$location服务,当我们通过$routeProvider和$locationProvider来配置的时候,其本质就是这些服务是通过provider创建的.

注意点:

1.同constant注意点1

2.同constant注意点2

3.provider返回的服务也可以被装饰,所以这里myConfig服务拥有了money属性.(装饰具体怎么用,下面会说);

六.装饰服务

其实通过上面这么多的例子,看也能看懂装饰是什么了...

app.config(function($provide){
$provide.decorator('name',function($delegate){
$delegate.money = '100w';
return $delegate
})
});

同样是通过config,在参数函数中注入$provider服务,$provider服务有个decorator方法,它接受两个参数,第一个参数'name',是要被装饰的服务的名字,第二个参数是一个函数,函数中注入$delegate,$delegate就是被装饰的服务的实例,然后在函数中操作$delegate,就相当于操作了该服务的实例.

注意:

1.最后一定要return $delegate,这样服务才算被装饰完成了.

2.constant服务是不能被装饰的.

栗子就不说了吧,上面的都是~

总结上面的内容:

1.服务的实例被注入到控制器以后,都是一个引用对象,无论被注入多少个控制器中,实际都指向同一个对象,所以,无论修改其中的哪一个,其它所有的服务都会被改变.

2.服务的实例被修改过后,ng不会自动同步,需要使用$scope.$watch()监测其变化并手动刷新视图.

3.constant服务不能通过decorator进行装饰.

4.一些固定的参数和方法,使用constant

5.可能被修改的参数和方法,使用value

6.通过逻辑处理后得到的参数或方法,使用factory

7.可以使用factory的也可以使用service,反之亦然(一般就是用factory)

8.可以手动配置参数的服务,使用provider

七.可以创建不同实例的服务

之前我们说到,所有的服务的实例都是引用对象,无论被注入多少个控制器中,实际都指向同一个对象,所以,无论修改其中的哪一个,其它所有的服务都会被改变.这就是ng服务的设计模式,一般不需要去改变,但如果有特殊需要,要能够每次注入控制器后得到新的实例,可以这样做:

我们给服务添加了一个方法,每次执行一次这个方法,都会创建一个新的实例,这样,虽然在控制器里注入的是服务实例还是同一个,但是在调用创建实例方法的时候,都会创建一个新的实例,然后就可以单独修改这个实例,而不会影响到其它控制器:如下

js:

var serviceApp = angular.module('serviceApp',[]);
serviceApp.controller('myCtrl',function($scope,myConfig){
var myConfigConstant = myConfig.create();
$scope.name = myConfigConstant.name;
$scope.age = myConfigConstant.age;
angular.extend(myConfigConstant,{love:'zxg'});
$scope.love = myConfigConstant.love;
$scope.id = myConfigConstant.getId();
$scope.$watch(myConfigConstant.name,function(){$scope.name = myConfigConstant.name;});
myConfigConstant.name = 'white_bunny';
});
serviceApp.controller('myOtherCtrl',function($scope,myConfig){
var myConfigConstant = myConfig.create();
$scope.love = myConfigConstant.love;
$scope.name = myConfigConstant.name;
$scope.$watch(myConfigConstant.name,function(){$scope.name = myConfigConstant.name;});
}); /************************创建实例的服务************************/
serviceApp.factory('myConfig',function(){
return {
//服务返回的对象有一个create方法,该方法每次被执行都会返回一个新的constructorFun实例
    create: constructorFun.createNew
}
});

//创建一个构造函数
function constructorFun(){
var myname = 'code_bunny';
var age = 12;
var id = 1;
this.name = myname;
this.age = age;
this.id = id
}

//给构造函数添加createNew方法,用于实例化一个constructorFun.
constructorFun.createNew = function(){
return new constructorFun()
};

//给构造函数添加原型的方法.使得它的实例可以继承.
constructorFun.prototype = {
getId: function(){
return this.id
}
};

运行效果: http://jsfiddle.net/fpoq4deo/1/

好了~五种服务类型就全部讲完啦~~~

全部代码托管: https://github.com/OOP-Code-Bunny/angular/tree/master/service

相关阅读: angular控制器的执行顺序和服务的注入情况

angular五种服务详解的更多相关文章

  1. rabbitmq五种模式详解(含实现代码)

    一.五种模式详解 1.简单模式(Queue模式) 当生产端发送消息到交换机,交换机根据消息属性发送到队列,消费者监听绑定队列实现消息的接收和消费逻辑编写.简单模式下,强调的一个队列queue只被一个消 ...

  2. redis 五种数据结构详解(string,list,set,zset,hash)

    redis 五种数据结构详解(string,list,set,zset,hash) Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存 ...

  3. redis 五种数据结构详解(string,list,set,zset,hash),各种问题综合

    redis 五种数据结构详解(string,list,set,zset,hash) https://www.cnblogs.com/sdgf/p/6244937.html redis 与 spring ...

  4. 【Redis】redis 五种数据结构详解(string,list,set,zset,hash)

    redis 五种数据结构详解(string,list,set,zset,hash) Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存 ...

  5. Android特效 五种Toast详解

    Toast是Android中用来显示显示信息的一种机制,和Dialog不一样的是,Toast是没有焦点的,而且Toast显示的时间有限,过一定的时间就会自动消失.而且Toast主要用于向用户显示提示消 ...

  6. 转 XMLHttpRequest().readyState的五种状态详解

    转 http://javathinker.blog.ccidnet.com/blog-htm-itemid-1262479-do-showone-type-blog-uid-36384.html 在& ...

  7. Odoo中的五种Action详解

    转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10826232.html Odoo中的五种action都是继承自ir.actions.actions模型实现的 ...

  8. Redis 五种数据结构详解(string,hash,list,set,zset)

    一.五种数据结构: 1. String--字符串 String 数据结构是简单的 key-value 类型,value 不仅可以是 String,也可以是数字(当数字类型用 Long 可以表示的时候e ...

  9. ajax readyState的五种状态详解

    通过ajax的readyState的值,我们可以知道当前的这个http请求处于什么状态.对于web的调试是比较重要的. readyState 状态说明: (0)未初始化 此阶段确认XMLHttpReq ...

随机推荐

  1. 【centos6】安装redis + phpredis 以及 常用配置参数

    1.redis-server和redis-cli安装文章:http://www.cnblogs.com/skyessay/p/6429988.html 1.前置条件:查看是否安装gcc,命令:gcc ...

  2. 在centos 5.5 i386 上安装 oracle 10g xe

    1.安装rpm包 nano /v.sh 将下面内容复制进去 #!/bin/bash rpm -Uvh compat-db-4.2.52-5.1.i386.rpm rpm -Uvh compat-lib ...

  3. Xcode使用小技巧-filter查找功能和查看最近修改的文件

    今天偶然发现了关于Xcode的一个小技巧: 1.查看最近修改的文件 2.使用filter查找制定文件 没错,就是下面这个东西,很容易忽略的一个小工具,在Xcode左下角位置. 通过这个,我们能够在整个 ...

  4. java+win7+eclipse+Maven+sikuli 配置总结---图形脚本语言

    简介:Sikuli 是一种新颖的图形脚本语言,或者说是一种另类的自动化测试技术.它与我们常用的自动化测试技术(工具)有很大的区别. 关于配置,一直是一个问题,下面做个总体介绍,用sikuli也有几个月 ...

  5. MFC的DLL中实现定时器功能

    方法一:创建一个线程, 反复读系统时间不就可以了? 如果定时要求不严,用Sleep就可以了.DWORD WINAPI TimerThread(LPVOID pamaram) { UINT oldTic ...

  6. log4j(三)——如何控制不同级别的日志信息的输出?

    一:测试环境与log4j(一)——为什么要使用log4j?一样,这里不再重述 二:老规矩,先来个栗子,然后再聊聊感受 import org.apache.log4j.*; //by godtrue p ...

  7. java多线程学习--java.util.concurrent (转载)

    题记:util和concurrent 包是后续重点先看的和学习的模块 原文地址:http://www.cnblogs.com/sunhan/p/3817806.html CountDownLatch, ...

  8. iOS证书(.p12)和描述文件(.mobileprovision)申请

    证书类型 使用场景 开发(Development)证书和描述文件 用于开发测试,在HBuilder中打包后可在真机环境通过Safari调试 发布(Distribution)证书和描述文件 用于提交Ap ...

  9. Oracle 12C -- 在相同的列的集合上创建多个索引

    在12C中,可以在相同的列的集合上创建多个索引,但是多个索引的类型要不同.同一时刻,只有一个是可见的. SQL> create table emp_tab as select * from em ...

  10. CTreeCtrl获得鼠标点击时的节点

    原文链接: http://blog.csdn.net/lcalqf/article/details/21321923 1.添加图标 HICON icon[10]; icon[0]=AfxGetApp( ...