服务 Service
服务这个概念其实并不陌生,比如在Java语言中便有这样的概念,其作用就是对外提供某个特定的功能,如消息服务,菜单服务等,是一个独立的模块。
angular的服务是这样定义的:
Angular services are singletons objects or functions that carry out specific tasks common to web apps.
在Angular中,服务的本质是一些和控制器捆绑在一起的一个单例对象或函数,对外提供特定的功能。通过这些对象提供了在应用的整个生命周期都存有数据的方法,当重载或刷新页面时,数据不会被清除,而且还与加载之前保持一致。即无论这个服务被注入到任何地方,对象始终只有一个实例。
这与我们自己定义一个function然后在其他地方调用不同,因为服务被定义在一个模块中,所以其使用范围是可以被我们管理的。angular的避免全局变量污染意识非常强。
内置服务
Angular提供了很多内置服务,如$scope、$http、$window、$location等。
我们介绍一下$location。(注意服务是如何注入控制器的,后边会有单独模块介绍angular的依赖注入)
获取url的相关方法:
<div ng-controller="MyController"> <div>当前的地址是: {{url}}</div> <button ng-click="onclick()">显示地址</button> </div> angular.module('MyApp').controller('MyController'['$scope','$location',function($scope,$location){ $scope.onclick = function () { $scope.url = $location.absUrl(); } }])
以'http://39.106.222.235:8080/cds/personalCenter/index.html#/demandManager/view.html?orderId=10&projectId=42&mWin=false' 这个路径为例:
1.获取当前完整的url路径:
$location.absUrl():
http://39.106.222.235:8080/cds/personalCenter/index.html#/demandManager/view.html?orderId=10&projectId=42&mWin=false
*2.获取当前url路径(当前url#后面的内容,包括参数和哈希值--哈希值介绍):
$location.url();
// /demandManager/view.html?orderId=10&projectId=42&mWin=false*3.获取当前url的子路径(也就是当前url#后面的内容,不包括参数):
$location.path()
// /demandManager/view.html
4.获取当前url的协议(比如http,https)
$location.protocol()
// http
5.获取当前url的主机名
$location.host()
// localhost
6.获取当前url的端口
$location.port()
// 80 (这里就是wamp的默认端口号)
*7.获取当前url的哈希值(hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分)
$location.hash()
// null
*8.获取当前url的参数的序列化json对象
$location.search()
// {'orderId':10,'projectId':42,'mWin':'false'}
修改url的相关方法:
于$location.search(),$location.url();,$location.path(),$location.hash(),这四种可以传入参数进行修改url,在这种情况下,函数的返回值都是$location本身:
1.修改url的子路径(也就是当前url#后面的内容,包括参数):
参数格式:字符串
$location.url('/demandCustomer/view.html?orderId=10&projectId=42&mWin=true'); //http://39.106.222.235:8080/cds/personalCenter/index.html#/demandCustomer/view.html?orderId=10&projectId=42&mWin=true
2.修改url的子路径部分(也就是当前url#后面的内容,不包括参数):
参数格式:字符串
$location.path('/demandCustomer/view.html'); //http://39.106.222.235:8080/cds/personalCenter/index.html#/demandCustomer/view.html?orderId=10&projectId=42&mWin=false
3.修改url的哈希值部分
参数格式:字符串
$location.hash('#footer'); //http://39.106.222.235:8080/cds/personalCenter/index.html#/demandManager/view.html?orderId=10&projectId=42&mWin=false#footer
4.修改url的参数部分(这是重点啊!!!!!!!!)
(1).传入两个参数,第一个参数的格式为字符串:
①第二个参数的格式也是字符串
第一个参数表示url参数的属性名,第二个参数是该属性名的属性值,如果是已有属性名,则修改,如果不是已有属性,则新增
$location.search('mWin','true') //http://39.106.222.235:8080/cds/personalCenter/index.html#/demandManager/view.html?orderId=10&projectId=42&mWin=true
②第二个参数的格式为数组,数组中的各个值也是字符串或者布尔值
第一个参数表示url参数的属性名,第二个参数是该属性名的值,有多少个值,url中就会依次重复出现多少个.如下:
$location.search('projectSearch',['book','home']) //http://39.106.222.235:8080/cds/personalCenter/index.html#/demandManager/view.html?orderId=10&projectId=42&mWin=false&projectSearch=book&projectSearch=home
(2).传入两个参数,第一个参数为字符串,第二个参数为null:
第一个值表示url参数的属性名,则删除该属性,那就等于没改过
$location.search('projectId',null) //http://39.106.222.235:8080/cds/personalCenter/index.html#/demandManager/view.html?orderId=10&mWin=false
(3).传入一个参数,格式为json对象:
直接用这个json对象里的键值对替换整个url的参数部分
①普通的键值对:
$location.search({orderId:11,projectId:45,mWin:true,projectSearch:'book'}) //http://39.106.222.235:8080/cds/personalCenter/index.html#/demandManager/view.html?orderId=11&projectId=45&mWin=true&projectSearch=book
②属性值为一个数组:
$location.search({orderId:11,projectSearch:['book','home']}) //http://39.106.222.235:8080/cds/personalCenter/index.html#/demandManager/view.html?orderId=11&projectId=45&mWin=true&projectSearch=book&projectSearch=home
(4).传入一个参数,格式为字符串:
直接用这个字符串替换整个url的参数部分(没有键值对,参数部分就是一个属性名)
$location.search('role') //http://39.106.222.235:8080/cds/personalCenter/index.html#/demandManager/view.html?role
不存入历史记录
在上文的所有修改url的方法的时候,每修改一次,url都会被存入历史记录,可以使用后退按钮回到修改前的url,如果不想要这种效果,而仅仅是替换当前的记录,可以使用:
$location.replace();
例子:
// 原url: // http://39.106.222.235:8080/cds/personalCenter/index.html#/demandManager/view.html?orderId=10&projectId=42&mWin=false $location.url('/demandCustomer/view.html?orderId=10&projectId=45&mWin=true'); // 修改一次后: // http://39.106.222.235:8080/cds/personalCenter/index.html#/demandCustomer/view.html?orderId=10&projectId=45&mWin=true // 按下后退回到原url: // http://39.106.222.235:8080/cds/personalCenter/index.html#/demandManager/view.html?orderId=10&projectId=42&mWin=false // 再按下前进回到修改后url: // http://39.106.222.235:8080/cds/personalCenter/index.html#/demandCustomer/view.html?orderId=10&projectId=45&mWin=true $location.path('/demandCustomer').replace(); // 修改第二次后调用replace(): // http://39.106.222.235:8080/cds/personalCenter/index.html#/demandCustomer/view.html?orderId=10&projectId=42&mWin=false // 按下后退,不会回到第二次修改前的url,而是回到第一次修改前的url // http://39.106.222.235:8080/cds/personalCenter/index.html#/demandManager/view.html?orderId=10&projectId=42&mWin=false
监听路径跳转
Angular也可以监听路径的变化,这些事件我们会在路由中做讲解。(待添加)
http请求
$http是Angular内置的服务,用于服务向服务器发送请求,应用响应服务器传送过来的数据。
写法:
写法一 $http({ method: 'GET',//可以改成POST url: '/someUrl' }).then(function successCallback(response) { // 请求成功执行代码 },function errorCallback(response) { // 请求失败执行代码 }); 写法二 $http.get('/someUrl',config).then(successCallback,errorCallback); $http.get('/someUrl',{params:{"id":3}}).then(successCallback,errorCallback); $http.post('/someUrl',data:{name:"aaa",id:"1",age:"20"},errorCallback); jsonp写法 $http({ method: 'JSONP',url: 'http://www.b.com/test.PHP?callback=JSON_CALLBACK' }).success(function(response){ console.log(response); }); $http.jsonp( 'http://www.b.com/test.PHP?callback=JSON_CALLBACK' ).success(function (response){ console.log(response); }); //这里要注意,跨域请求的url后一定要追加参数callback,并且callback的值是固定的,即JSON_CALLBACK,不要去做任何改动
这里只做简单介绍,项目中我们使用的是基于$http的$resource服务,$resource依赖于$http。创建一个resource对象的工厂函数,可以让你安全的和RESFUL服务端进行数据交互。
需要注入 ngResource 模块。同样会有单独模块介绍。这里是连接(待添加)。
延时器与计时器
J是中的setTimeout()和setInterval()这个大家肯定不会陌生,在Angular中也有同样的服务
$timeout 服务
angular.module('myApp').controller('myCtrl',$timeout) { $scope.myHeader = "Hello World!"; $timeout(function () { $scope.myHeader = "How are you today?"; },2000); }); //要在模块中引入
$interval 服务
angular.module('myApp').controller('myCtrl',$timeout) { $scope.theTime = new Date().toLocaleTimeString(); $interval(function () { $scope.theTime = new Date().toLocaleTimeString(); },1000); //时间绑定。 });
自定义服务
创建自定义服务
3种创建自定义服务的方式。
- Factory
- Service
- Provider
之前也没有提到过,Angular这套框架最开始是由后台人员开发的,应用了后台早就存在的分层思想。所以项目也是使用这一设计模式。由此创建自定义服务时用到了上述三种方式。
Factory
factory方式创建的服务,作用就是返回一个有属性有方法的对象。
//通过工厂模式创建自定义服务 //同样可以注入依赖,但不能注入$scope作用域对象。 angular.module("MetronicApp").factory('myFactory',['$resource','URLconfig',function($resource,URLconfig) { var service = {};//定义一个Object对象' service.name = "龙傲天"; service._getUrl = URLconfig.url; var age;//定义一个私有化的变量 //对私有属性写getter和setter方法 service.setAge = function(newAge){ age = newAge; } service.getAge = function(){ return age; } //原始需求详情 service.getUrl = function (id) { return $resource(service._getUrl).get({orderId: id}); }; return service;//返回这个Object对象 }]); //创建控制器 angular.module("MetronicApp").controller('myCtrl',['$scope','myFactory',myFactory) { $scope.name = myFactory.name; myFactory.setAge(20); $scope.age =myFactory.getAge(); myFactory.getUrl(id).$promise.then(function (result) { scope.model = result.data; });; }]);
service
在service使用构造函数的方法去用它,在控制器中是以new的方式引入,所以可以调用 service中定义的属性
//通过工厂模式创建自定义服务 //依赖注入其他模块 angular.module("MetronicApp").controller("myCtrl",['$scope'',myService',myService){ $scope.name=myService.name; myService.get(id).$promise.then(function (result) { $scope.model = result.data; }); }]) /*Service是new出来的,所以可以直接调用里面的属性*/ angular.module("MetronicApp").service("myService",URLconfig){ this.name = '龙傲天'; this.url = URLconfig.url; this.get = function (id) { return this.$resource(this._getUrl).get({orderId: id}); }; }])
Provider
如果想在 service 对象启用之前,先进行模块范围的配置,那就应该选择 provider。
当你使用Provider创建一个自定义服务时,可以通过$get()函数返回内容,唯一可以写进config配置阶段的一种。如果服务,必须要在配置阶段执行,那么必须使用provider。
使用Provider的优点就是,你可以在Provider对象传递到应用程序的其他部分之前在app.config函数中对其进行修改。
//名字必须符合规范:myProvider(你的Provider服务名字)+Provider angular.module("MetronicApp").config(function(myProviderProvider){ myProviderProvider.name = "龙傲天" }) //使用$get方法关联对应的config angular.module("MetronicApp").provider("myProvider",URLconfig){ this.$get = function(){ return { name : this.name,age : 18,url : URLconfig.url,getData : function(id){ return this.$resource(URLconfig.url).get({orderId: id}); } } } }]) angular.module("MetronicApp").controller("myCtrl",'myProvider',myProvider){ $scope.model = { name : myProvider.name,age : myProvider.age,url : myProvider.url,} myProvider.getData(id).$promise.then(function (result) { $scope.model.data = result.data; }); }])
constant和value
使用constant和value方法创建服务,常用于返回一个常量。
angular.module("MetronicApp").constant('$age',{ age: '18' }); angular.module("MetronicApp").value('$name',{ name : '龙傲天' }); angular.module("MetronicApp").controller('MyController',$age,$name){ $scope.name = $name.name $scope.USD = $age.age })
过滤器中使用自定义服务
angular.module("MetronicApp").service("myService",['URLconfig',function(URLconfig){ this.name = '龙傲天'; this.url = URLconfig.url; this.getUrl = function (url) { var arr=url.split('/'); arr.push(this.url); arr.push(this.name); return arr.join('/') } }]) app.filter('myFilter',['myService',function(myService) { return function(url) { return myService.getUrl(url); }; }]);
管理服务的依赖
添加自定义服务依赖项方法
我们在自定义服务时,会依赖对象或服务,有下面三种方式:
(1) 隐式声明
在参数中直接调用,但这种方式在代码压缩时注入的对象可能会失效
angular.module("MetronicApp").service("myService",URLconfigService) { //code })
function myService($resource,URLconfigService) { //code } myService.$inject = ['$resource','URLconfigService']; angular.module('MetronicApp').service('myService',myService);
(3) 显式声明
在创建服务的函数中,添加一个数组,在数组中按顺序声明需要注入的服务或对象名称,这种方式既高效也不会丢失代码,推荐使用(在上述实例中我们是用的都是此方法)
angular.module("MetronicApp").service("myService",URLconfig){ this.name = '龙傲天'; this.url = URLconfig.url; this.get = function (id) { return this.$resource(this._getUrl).get({orderId: id}); }; }])
在angular的modul(模块)中注入服务,也可以在定义modul时使用数组作为第二个参数,在此处把服务注入进去,这样在函数体中使用不一致的服务名称也是可以的,不过要确保注入的顺序是一致的。(这个作为穿插,项目中可以使用定义的服务名称,也能定义简化参数名称)注意此方法只针对显式声明有效。
angular.module("MetronicApp").controller("myCtrl",myDemandManageService',myService){ //这里的myService就是指myDemandManageService; $scope.name=myService.name; myService.get(id).$promise.then(function (result) { $scope.model = result.data; }); }])
嵌套注入服务
在Angular中,有时需要将一个自定义的服务注入到另一个自定义的服务中,形成嵌套注入的形式,通常只需要将被注入的服务作为内置服务,采用显式声明的方式注入即可。
// 使用factory定义confirm服务
angular.module("MetronicApp").factory('confirm',['$window',function ($win) { return function (msg) { $win.confirm(msg); } }]); // 将confirm服务显式的注入到fontWay服务中 angular.module("MetronicApp").service('fontWay','confirm',function ($win,con) { return function (t,msg) { return (t) ? con(msg) : $win.alert(msg); } }]); angular.module("MetronicApp").controller('MyCtrl','fontWay',function ($scope,fontWay) { $scope.ask = function (t,msg) { fontWay(t,msg); } }])
服务的其他配置
创建好的服务一般比较复杂,如果后期需要修改,往往面临很高的风险,Angular为服务添加了一些设置项,如装饰器(decorator),可以在不修改原代码的情况下为服务添加其他功能。
服务的装饰器(decorator)
装饰器(decorator)是Angular中内置服务$provide所特有的一项设置函数,通过它可以拦截服务在实例化时创建的一些功能,并对原有功能进行优化和替代。
// 使用工厂函数factory定义myService服务
angular.module("MetronicApp").factory("myService",function () { return { name: '龙傲天',age: 18 } }); // 使用$provider的装饰器decorator为服务myService扩展一个title属性 //关键的地方是在于decorator方法的回调函数的参数$delegate,指的是当前的数据(继承);项目中可以使用这个来做你想做的事情。 angular.module("MetronicApp").config(function ($provide) { $provide.decorator('myService',function ($delegate) { $delegate.title = 'CDS'; return $delegate; }) }); angular.module("MetronicApp").controller('MyCtrl',myService) { $scope.model = myService; });
服务是Angular的一个难点,大家可以先理解,在项目中参照上述实例做比较。。任重而道远啊。。