angularJS---service
service
ng的服务是这样定义的:
Angular services are singletons objects or functions that carry out specific tasks common to web apps.
它是一个单例对象或函数,对外提供特定的功能。
1.首先是一个单例,即无论这个服务被注入到任何地方,对象始终只有一个实例。
2.其次这与我们自己定义一个function然后在其他地方调用不同,因为服务被定义在一个模块中,所以其使用范围是可以被我们管理的。ng的避免全局变量污染意识非常强。
个人感觉service就像模块,把方法封装好,注入调用就行了。
首先先介绍一下ng内置的服务
ng内置的服务都是以$开头的。如$scope
常用的内置服务:
A. $scope: $scope(作用域)是angular应用程序中视图(HTML)和控制器(JavaScript)之间的纽带。也就是说html和javascript可以通过它来传输、共享数据,跟别的服务不同,它只用在controller上。
B. $http: $http服务是封装了ajax方法的用于请求数据的一个服务
php代码:
<?php if($_GET["name"]=="jjk" && $_GET["age"]=='18'){ $data = "welcome to you"; echo $_GET["callback"]."({data: '".$data."'})";//使用jsonp请求时 callback是ng内部自动生成的 }else{ $data = "sorry!!!"; echo $_GET["callback"]."({data: '".$data."'})";//返回一个对象 } ?>
jsonp请求:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body ng-app="app"> <div ng-controller="myCtrl"> <input ng-model="name"> <input ng-model="age"> <button ng-click="submitJsonp()">Jsonp</button> <br> <div ng-bind='data'></div> </div> </body> <script src="./js/angular.js"></script> <script> angular.module('app', []) .controller('myCtrl', function ($scope, $http) { $scope.name = 'jjk'; $scope.age = 18; $scope.submitJsonp = function () { $http({ url: './php/jsonp.php', method: 'JSONP', params: { name: $scope.name, age: $scope.age } }).then(function (data) { console.log(data); $scope.data = data.data.data; }) } }) </script> </html>
在chrome中显示:
php代码:
<?php if($_GET["name"]=="jjk"){ echo "1"; }else{ echo "2"; } ?>
get请求:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body ng-app="app"> <div ng-controller="myCtrl"> <input ng-model="name"> <input ng-model="age"> <button ng-click="submitGet()">Jsonp</button> <br> <div ng-bind='data'></div> </div> </body> <script src="./js/angular.js"></script> <script> angular.module('app', []) .controller('myCtrl', function ($scope, $http) { $scope.name = 'jjk'; $scope.age = 18; $scope.submitGet = function () { $http({ url: './php/get.php', method: 'GET', params: { name: $scope.name, age: $scope.age } }).then(function (data) { console.log(data); $scope.data = data.data; }) } }) </script> </html>
在chrome中显示:
post请求在angular中就变的有点麻烦了。按照之前jquery的post请求方式
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>jquery的post请求</title> </head> <body> <button id="btn">post</button> </body> <script src="./js/jquery.js"></script> <script> $('#btn').on('click', function(){ $.ajax({ url:'./php/post.php', method: "POST", data:{user:'jjk',password:'123456'}, datatype:'json', success: function(data){ console.log(data); } }); }); </script> </html>
php代码:
<?php echo $_POST["user"]; echo $_POST["password"]; ?>
在chrome中显示:
看一下他的请求头:
重点看红框的内容:application/x-www-form-urkencoded. 从客户端到服务器端数据传输编码标准就是application/x-www-form-urlencoded 这种数据的基本形式是采用"&"分割开的key/value例如: name1=value1&name2=value2&name3=value3&.....&nameN=valueN 跟get请求的参数是一样的,不同在于,get请求是放在url上,而post请求把数据放在请求头上。 等一下,我们明明传递的是一个对象啊!data:{name:'jjk',password:'123456'}. 其实jquery内部(下面会提到)已经帮我们转换好了数据
现在来看一下,angular的post请求是如何做的:
php代码:
<?php // echo $_POST["user"]; // echo $_POST["password"]; echo $_POST["input"]; echo $_POST["skill"]; ?>
post请求:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body ng-app="app"> <div ng-controller="myCtrl"> <input ng-model="input"> <input ng-model="skill"> <button ng-click="submitPost()">post</button> <br> <div ng-bind='data'></div> </div> </body> <script src="./js/angular1.5.9.js"></script> <!--<script src="./js/ngPost.js"></script>--> <script> angular.module('app',[]) .controller('myCtrl', function ($scope, $http) { $scope.submitPost = function () { $http({ url: './php/post.php', method: 'POST', params: { input: $scope.input, skill: $scope.skill } }).then(function (data) { console.log(data); // $scope.data = data.data; }) } }) </script> </html>
在chrome中显示:
咦?我明明是post请求啊!!!! 怎么变成了get请求的格式了?
其实,这里有个小坑。pramas适用情况:get类型请求(注:jsonp也是get类型的请求)。 所以他不适用post请求。要post请求请使用data
那就改一下:
再看一下network:
问题就出来了:angular发送的请求数据是json数据,后台直接使用$_POST是读取不到数据的。$POST方式只能接收Content-Type: application/x-www-form-urlencoded提交的数据。从这里可以看出来,jquery是比ng要有“灵性”啊,哎,搞不懂ag开发人员是什么想法。
解决的办法也有:1.从后台着手:想办法让后台读取json数据,再返回数据。
php后台有个方法: file_get_contents(
"php://input"
,
true
); ‘php://input’是个可以访问请求的原始数据的只读流。只读取Content-Type不为multipart/form-data的数据.(详细:http://blog.csdn.net/qiufengwuqing123/article/details/53707840) 我们正好适合这种情况,来试一下:
改一下php代码:
<?php $res = file_get_contents("php://input",true); echo $res; ?>
前端代码不变。请求一下数据:
在chrome中显示:
通过修改php去获取数据,嗯,不太好,我们是前端啊,如果数据不能正常返回给后台,是会被后台人员取笑的,哈哈哈哈。还是看看前端有什么方法。
2.现在来说一下前端的方法:1.修改请求头。
控制台显示:
虽然改了请求头,但发送的数据有问题,变成了字符串。有2个方案:a.改变data的数据格式为字符串 弊端:只能传一个值到后端;b.使用jq的param方法,将数据序列化再传给后台处理 ,弊端:需要引入jq文件
先来看一下a方案:只需将 data改成这样:data:‘input=jjk’即可
在chrome中显示:
b方案:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body ng-app="app"> <div ng-controller="myCtrl"> <input ng-model="input"> <input ng-model="skill"> <button ng-click="submitPost()">post</button> <br> <div ng-bind='data'></div> </div> </body> <script src="./js/angular1.5.9.js"></script> <script src="./js/jquery.js"></script> <!--<script src="./js/ngPost.js"></script>--> <script> angular.module('app', []) .controller('myCtrl', function ($scope, $http) { $scope.submitPost = function () { $http({ url: './php/post.php', method: 'POST', data: { input: $scope.input, skill: $scope.skill }, transformRequest: function (data) { return $.param(data); }, headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).then(function (data) { console.log(data); // $scope.data = data.data; }) } }) </script> </html>
在chrome中显示:
后台返回的数据:
其实自己也可以写一个类似的param方法;无非就是对data的json数据处理成类似这样的模式:input=jjk&skill=ps 使用循环和字符串方法:
function transformRequest(data){
var str = '';
for(var i in data){
str += i + '=' + data[i] + '&';
}
return str.substring(0,str.length-1);
}
跟param方法没什么区别,这样就可以不用引入jquery(建议将此方法写到commonjs上,有备无患)。
再来介绍第2种方法:直接修改angular,不惯他这“臭毛病”。
修改Angular的$httpProvider的默认处理
angular.module('MyModule', [], function($httpProvider) { // Use x-www-form-urlencoded Content-Type $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8'; /** * The workhorse; converts an object to x-www-form-urlencoded serialization. * @param {Object} obj * @return {String} */ var param = function(obj) { var query = '', name, value, fullSubName, subName, subValue, innerObj, i; for(name in obj) { value = obj[name]; if(value instanceof Array) { for(i=0; i<value.length; ++i) { subValue = value[i]; fullSubName = name + '[' + i + ']'; innerObj = {}; innerObj[fullSubName] = subValue; query += param(innerObj) + '&'; } } else if(value instanceof Object) { for(subName in value) { subValue = value[subName]; fullSubName = name + '[' + subName + ']'; innerObj = {}; innerObj[fullSubName] = subValue; query += param(innerObj) + '&'; } } else if(value !== undefined && value !== null) query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&'; } return query.length ? query.substr(0, query.length - 1) : query; }; // Override $http service's default transformRequest $httpProvider.defaults.transformRequest = [function(data) { return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data; }]; });
将它打包在ngPost.js文件里,使用是引进他,并注入到主模块即可。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body ng-app="app"> <div ng-controller="myCtrl"> <input ng-model="input"> <input ng-model="skill"> <button ng-click="submitPost()">post</button> <br> <div ng-bind='data'></div> </div> </body> <script src="./js/angular1.5.9.js"></script> <!--<script src="./js/jquery.js"></script>--> <script src="./js/ngPost.js"></script> <script> angular.module('app', ['MyModule']) .controller('myCtrl', function ($scope, $http) { $scope.submitPost = function () { $http({ url: './php/post.php', method: 'POST', data: { input: $scope.input, skill: $scope.skill }, // transformRequest: function (data) { // return $.param(data); // }, // headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).then(function (data) { console.log(data); // $scope.data = data.data; }) } }) </script> </html>
在chrome中显示:
返回的数据:
好了!!! $http服务总算总结完了!!!!!!
C. $timeout $interval : 封装自原生js的2个定时器。使用方式没区别
D. $location: 封装自原生js的location。$location服务解析在浏览器地址栏中的URL(基于window.location)并且让URL在你的应用中可用。改变在地址栏中的URL会作用到$location服务,同样的,改变$location服务也会改变浏览器的地址栏。(可以使用$location进行重定向等操作)
调用$location服务:
暴露浏览器地址栏中的URL,让你可以:
1.监察URL。
2. 改变URL。
与浏览器同步URL,当:
1.改变地址栏。
2.单击『前进』『后退』或一个历史记录中的链接。
3.打开一个链接。
4.将URL对象表示为一个方法集。 (protocol, host, port, path, search, hash)
内置方法:
-
- absUrl( ):只读;根据在RFC 3986中指定的规则,返回url,带有所有的片段。
- hash( ):读、写;当带有参数时,返回哈希碎片;当在带有参数的情况下,改变哈希碎片时,返回$location。
- host( ):只读;返回url中的主机路径。
- path( ):读、写;当没有任何参数时,返回当前url的路径;当带有参数时,改变路径,并返回$location。(返回的路径永远会带有/)
- port( ):只读;返回当前路径的端口号。
- protocol( ):只读;返回当前url的协议。
- replace( ):如果被调用,就会用改变后的URL直接替换浏览器中的历史记录,而不是在历史记录中新建一条信息,这样可以阻止『后退』。
- search( ):读、写;当不带参数调用的时候,以对象形式返回当前url的搜索部分。
- url( ):读、写;当不带参数时,返回url;当带有参数时,返回$location。
内置事件:
-
- $locationChangeStart:在URL改变前发生。这种改变可以通过调用事件的preventDefault方法为阻止。成功时触发$locationChangeSuccess事件。
- $locationChangeSuccess:当URL改变后发生。
使用:
何时使用$location
任何你想要改变当前URL的时候,都可以使用$location。
$location不会做
当浏览器的URL改变时,不会重新加载整个页面。如果想要重新加载整个页面,需要使用$window.location.href。
E. $document $document服务就和angular.element(document)是一样的,是一个整体的dom结构树,包含jqlite的所有方法 另外, $document[0]和原生JS的document等效
AngularJs是不直接操作DOM的,但是在平时的开发当中,我们有的时候还是需要操作一些DOM的,如果使用原生的JS的话操作过于麻烦
所以$document服务可以代替原生document去使用内置的jqlite方法。
F. $window 跟原生window没什么两样。
G.$sce $sce服务是“Strict Contextual Escaping”的缩写。翻译成中文就是“严格的上下文模式”也可以理解为安全绑定.由于ng是使用指令绑定数据,所以后台返回的数据经常会带有标签。但有些标签会可能会对页面造成不可预期的破坏(脚本攻击之类的),所以$sce服务就是为了过滤‘威胁’标签的,再通过ng-bind-html来插入标签。 $sce服务有个方法trustAsHtml()---用于信任后台返回的标签。
实例:
app.filter("html",function($sce){
return function(input){
return $sce.trustAsHtml(input)
}
})
H. $index $index只在ng-repeat中起作用,它的作用是获取当前标签的索引值。除了$index外还有$first,$middle,$last,$even,$odd。
常用的内置服务大概就是这些了。
自定义服务:ng支持开发者自己定义服务 。开发者使用provider/factory/service/constant/value 等函数创建一个自定义服务。开发者自定义的服务同样可以注入。以下类型的函数可以接受注入: controller/directive/filter/service/factory等(ng中的‘依赖注入’是通过provider和injector这2个机制联合实现的)
虽然,使用provider/factory/service/constant/value 等函数均可创建一个自定义服务,但它们之间的区别还是要了解一下:provider是基础,其余函数都是调用provider函数实现,而且从左到右,灵活性越来越差(可以翻源码看看) 但建议使用service(他本身就是服务的意思嘛)就可以了。话虽如此,还是要好好了解这些函数:
先说这些函数之前,我们先看看module里有什么
<script src="./AngularJs/js/angular.js"></script> <script> var app = angular.module('app',[]); console.log(app); </script>
在chrome中显示:
大多数函数我们都是知道的。
现在先来了解一下:provider 本身就有:‘提供者’的意思。
provider自定义服务的方式:
<body ng-app="app"> <div ng-controller="myCtrl"> <p>{{name}}</p> <button ng-click='say()'>点击</button> </div> </body> <script src="./js/angular.js"></script> <script> angular.module('app',[]) .controller('myCtrl',['$scope','myService',function($scope,myService){ console.log(myService); $scope.name = myService.name; $scope.say = myService.say; }]) .provider('myService',function(){ return{ $get:function(){ return { name: 'jjk', say: function(){ console.log(this.name) } } } } }) </script>
返回一个对象,对象有$get方法 又返回一个对象,这个对象才是真正的定制服务的内容,其他都是固定写法。其中的$get方法是用来返回provider实例的(说了跟没说一样)
来看一下源码:
function provider(name, provider_) { assertNotHasOwnProperty(name, 'service'); if (isFunction(provider_) || isArray(provider_)) { provider_ = providerInjector.instantiate(provider_); } if (!provider_.$get) { throw $injectorMinErr('pget', 'Provider \'{0}\' must define $get factory method.', name); } return (providerCache[name + providerSuffix] = provider_); }
第一个参数是自定义服务的名称,第二个参数可以是一个function、array、object 从
if (!provider_.$get) {
throw $injectorMinErr('pget', 'Provider \'{0}\' must define $get factory method.', name);
}
可以看出,每个prodider都有个$get方法,不然会报错的。
factory: 从名字上了解,‘工厂’的意思嘛。
先看一下他的源码:
function factory(name, factoryFn, enforce) { return provider(name, { $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn }); }
从源码可以看出 返回一个provider,所以说factory本质上就是由provider实现的。换句话说:factory只是生成provider的快捷方式而已。那factory又是如何自定义服务的呢?
<script src="./js/angular.js"></script> <script> angular.module('app',[]) .controller('myCtrl',['$scope','myService',function($scope,myService){ console.log(myService); $scope.name = myService.name; $scope.say = myService.say; }]) .factory('myService', function(){ return{ name:'jjk', say: function(){ console.log(this.name); } } }) </script>
直接返回一个对象,而这个对象就是我们自定服务的内容。
接下来就是 service了 ‘服务’
看一下源码:
function service(name, constructor) { return factory(name, ['$injector', function($injector) { return $injector.instantiate(constructor); }]); }
这个就有点意思了。service先调用了factoty,factoty又调用了provider。 那个factoty又有什么不同呢?
<script src="./js/angular.js"></script> <script> angular.module('app',[]) .controller('myCtrl',['$scope','myService',function($scope,myService){ console.log(myService); $scope.name = myService.name; $scope.say = myService.say; }]) .service('myService', function(){ this.name='jjk'; this.say=function(){ console.log(this.name); }; })
service有点构造函数的意思。注入时,ng自动会帮它‘new’一个实例出来。其实service也是可以写成factoty的形式
<script src="./js/angular.js"></script> <script> angular.module('app',[]) .controller('myCtrl',['$scope','myService',function($scope,myService){ console.log(myService); $scope.name = myService.name; $scope.say = myService.say; }]) .service('myService', function(){ return { name:'jjk', say: function(){ console.log(this.name); } } }) </script>
没什么影响的。这也是为什么推荐使用service创建自定义服务!!
再来看value ‘值’的意思嘛 value(name,value) name为注册的常量的名字,value为注册的常量的值或对象。
它的源码:
function value(name, val) { return factory(name, valueFn(val), false); }
很简单的一句代码,还是用了factoty(这大概就是factoty命名的由来吧)。
<script src="js/angular.js"></script> <script> var app = angular.module("demo", []); console.log(app) app.controller("indexCtrl", function($scope,author,six) { $scope.name = "value"; console.log(author) console.log(six) }) app.value("author","laoxie"); app.value("six",6) </script>
简单来说,value方法是用来设置保存一些数据的 它是可以被更改的,常用在baseUrl,文件头上。
最后一个 constant 译为:不变的,永恒的 跟value类似,但又不同。一旦设置了,就不能更改了。
源代码:
function constant(name, value) { assertNotHasOwnProperty(name, 'constant'); providerCache[name] = value; instanceCache[name] = value; }
从源代码可以看出来:constant跟 provider没什么关系 但他自定义服务跟value一样。
<script src="js/angular.js"></script> <script> var app = angular.module("demo", []); console.log(app) app.controller("indexCtrl", function($scope,author) { $scope.name = "value"; console.log(author); }) app.constant("author",123); </script>
服务这一块总结的差不多了。但我有个疑问:ng内部到底是如何实现注入服务的??
有机会一定要通读一下源码
2017/4/16 20:23