在上一篇文章中,我们主要学习了AngularJS的生命周期和MVC模式的使用,以及常见的ng-click指令、ng-controller指令、ng-model指令。和入门实战,本章我们将继续学习AngularJS的数据双向绑定、服务(Services)等知识。
AngularJS的数据双向绑定
在MVC一节中,通过几个示例介绍了如何创建和使用Controller、Model,如何与View层交互,其实也引出了AngularJS的一个重要特性。在Controller小节的例子中,通过点击按钮由Controller更改数据模型并将其展示在页面中,这是通过数据模型的变化从而影响视图层的显示。在Model小节的例子中,通过修改输入框中的值,由Controller捕获并更新对应的数据模型,这是通过视图层的变化从而影响数据模型的值。这就是AngularJS的数据双向绑定特性。
AngularJS的服务(Services)
AngularJS中一个重要的概念是服务,这个服务的概念比较宽泛,比如一个常量值也算做一个服务,既提供一个不可变值的服务。变量、对象、函数都算做是服务。在AngularJS中内置了好几十个服务,这些内置的服务都以$符号开头,比如$scope、$http、$log、$timeout、$interval等等,从字面意思都不难理解它们的作用,更多的内置服务可以去AngularJS官网查看API文档。
AngularJS的服务特征
AngularJS中的服务有两个主要特点:
- 延迟加载,当应用中的其他组建使用服务时才会实例化。
- 单例,在应用的整个生命周期中,一个服务只存在一份实例,所以服务一般用来共享可复用的代码逻辑或者数据。
AngularJS的自定义服务
除了内置的服务,我们还可以创建自己的服务,在AngularJS中我们可以通过$provide这个内置的服务来创建我们的自定义服务,$provide服务提供了五个方法供我们创建不同应用场景的自定义服务,这五个方法分别是provider(name, provider)、factory(name, $getFn)、service(name, constructor)、value(name, value)、constant(name, value)。
Value
我们先从value(name, value)这个方法看起,该方法有两个参数:
- 第一个参数为服务的名称,类型为字符串。
- 第二个参数可以是字符串、数字、数组、对象或者函数。
假设在我们的应用中,多个Controller中都使用了相同的属性,比如都需要用到客户端ID这个属性,那么我们可以将其抽象为一个服务,该服务就专门用来获取客户端ID,来看看如何创建这个服务:
var mainModule = angular.module("mainModule", []); mainModule.value("clientId", "qazxsw123456");
上面的示例代码创建了名为clientId的服务,该服务其实就是一个字符串。不过这和$provide服务有什么关系呢?其实上面这种写法并不是完整的写法,只是一个语法糖而已,真正完整的写法是在模块的.config()方法中通过$provide服务去创建:
mainModule.config(function($provide) { $provide.value("clientId", "qazxsw123456"); });
创建好服务后通过AngularJS的注入机制将其注入到Controller中:
mainModule.controller("FirstController", ["$scope", "clientId", function($scope, clientId) { $scope.clientId = clientId; }]); mainModule.controller("SecondController", ["$scope", "clientId", function($scope, clientId) { $scope.clientId = clientId; }]);
然后在HTML页面中正常使用Controller就可以了:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Demo for Value Service</title> <script src="../angular-1.5.8.js"></script> <script src="modules.js"></script> </head> <body ng-app="mainModule"> <div ng-controller="FirstController"> Client ID in FirstController: {{ clientId }} </div> <div ng-controller="SecondController"> Client ID in SecondController: {{ clientId }} </div> </body> </html>
上文中说过$scope服务的其中一个作用就是给Controller添加属性和方法,然后可以在绑定Controller的DOM中使用{{}}语法直接访问添加的属性或调用方法。然而就$scope服务的这一功能而言,AngularJS还提供了另一种方式,我们先来看看Controller的写法:
mainModule.controller("FirstController", ["clientId", function(clientId) { this.clientId = clientId; }]); mainModule.controller("SecondController", ["clientId", function(clientId) { this.clientId = clientId; }]);
上述代码中我们并没有将$scope服务注入到这两个Controller中,而是使用this创建了clientId属性,this代表Controller的实例。使用这种方式后在HTML页面中使用Controller也有点变化:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Demo for Value Service</title> <script src="../angular-1.5.8.js"></script> <script src="modules.js"></script> </head> <body ng-app="mainModule"> <div ng-controller="FirstController as first"> Client ID in FirstController: {{ first.clientId }} </div> <div ng-controller="SecondController as second"> Client ID in SecondController: {{ second.clientId }} </div> </body> </html>
我们看到在ng-conroller标签中不再是直接写Controller名称了,而是使用as关键字声明了Controller的实例,然后在{{}}中使用Controller的实例去访问属性或者调用方法。
使用$scope服务和this给Controller添加属性或方法的效果是一样的,所以不存在谁好谁坏的概念,只不过使用this的方式更贴合OO的思想,而且在HTML代码中对使用的属性或方法有更直观的可读性,能一眼看到使用了哪个Controller的属性或方法,所以使用哪种方式按个人喜好,但是不建议混用这两种方式。
这里在介绍另外一个语法糖,那就是在注入服务的时候不用繁复的在数组中和函数参数中都声明,只需要在函数的参数里声明就可以了:
mainModule.controller("FirstController", function($scope, clientId) { $scope.clientId = clientId; }); // 或者 mainModule.controller("FirstController", function(clientId) { this.clientId = clientId; });
Constant
我们再来看看constant(name, value)方法:
var mainModule = angular.module("mainModule", []); mainModule.constant("clientId", "qazxsw123456");
该方法和value(name, value)在创建的服务内容形式上来说是一样的,但是两者创建的服务在功能性上还是有区别的:
-
从名称就可以看出用
constant(name, value)
方法创建的服务是不可修改的。 -
使用
constant(name, value)
创建的服务可以在模块的.config()
方法中注入,也就是可以在创建其他服务时使用,而使用value(name, value)
创建的服务不可以。
Service
现在又有一个需求,希望能获取到当前时间添加在客户端ID后面,那么我们可以使用service(name, constructor)方法来创建获取当前时间的服务:
var mainModule = angular.module("mainModule", []); mainModule.value("clientId", "qazxsw123456"); mainModule.service("currentDate", Date); mainModule.controller("FirstController", function(clientId, currentDate) { this.clientId = clientId + "-" + currentDate; });
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Demo for Service Service</title> <script src="../angular-1.5.8.js"></script> <script src="modules.js"></script> </head> <body ng-app="mainModule"> <div ng-controller="FirstController as first"> Client ID in FirstController: {{ first.clientId }} </div> </body> </html>
输出结果:
Client ID in FirstController: qazxsw123456-Thu Sep 08 2016 17:05:30 GMT+0800 (CST)
service(name, constructor)方法的第二个参数是函数构造器,也就是函数的实例,所以currentDate服务的实体其实就是new Date()。
Factory
现在,我们希望通过一个服务就可以完成客户端ID和当前时间的拼接,不需要给Controller注入两个服务,来看看如何用factory(name, $getFn)方法来实现:
var mainModule = angular.module("mainModule", []); mainModule.constant("clientId", "qazxsw123456"); mainModule.factory("clientIdAndCurrentDate", function(clientId) { return clientId + "-" + new Date(); }); mainModule.controller("FirstController", function(clientIdAndCurrentDate) { this.clientId = clientIdAndCurrentDate; });
首先我们需要用constant(name, value)方法创建clientId服务,因为需要将它注入到新的服务中,前文也介绍过constant(name, value)和value(name, value)方法的区别。然后使用factory(name, $getFn)方法创建clientIdAndCurrentDate服务,该函数的第二个参数类型是函数,我们在该函数中将clientId服务返回的客户端ID与Date构造器返回的时间进行拼接然后返回,当然运行结果还是一样的:
Client ID in FirstController: qazxsw123456-Thu Sep 08 2016 17:05:30 GMT+0800 (CST)
其实这个服务还可以写成这样:
mainModule.constant("clientId", "qazxsw123456"); mainModule.service("currentDate", Date); mainModule.factory("clientIdAndCurrentDate", function(clientId, currentDate) { return clientId + "-" + currentDate; });
Povider
现在又有新的需求,希望对clientId后面的时间进行格式化,但假设我们没有权限去更改clientIdAndCurrentDate服务,那么这时我们需要使用provider(name, provider)方法创建另外一个服务,然后对clientIdAndCurrentDate服务进行配置,来看看如何实现这个服务:
var mainModule = angular.module("mainModule", []); mainModule.constant("clientId", "qazxsw123456"); mainModule.service("currentDate", Date); mainModule.factory("clientIdAndCurrentDate", function(clientId, currentDate) { return clientId + "-" + currentDate; }); mainModule.provider("clientIdAndCurrentDateByFormat", function() { this.formatFunc = function(str) { var clientId = str.substring(0, str.indexOf("-")); var dateStr = str.substring(str.indexOf("-"), str.length); var dateObj = new Date(dateStr); var year = dateObj.getFullYear().toString(); var month = (dateObj.getMonth() + 1).toString(); var day = dateObj.getDate().toString(); var hour = dateObj.getHours().toString(); var minute = dateObj.getMinutes().toString(); var second = dateObj.getSeconds().toString(); return clientId + "-" + [year, (month >= 10 ? month : 0 + month), (day > 10 ? day : 0 + day), hour, minute, second].join(""); }; this.$get = function(clientIdAndCurrentDate) { return this.formatFunc(clientIdAndCurrentDate); }; }); mainModule.controller("FirstController", function(clientIdAndCurrentDateByFormat) { this.clientId = clientIdAndCurrentDateByFormat; });
首先我们创建了formatFunc()辅助配置函数,然后实现了$get方法,通过formatFunc()辅助函数配置clientIdAndCurrentDate服务,我们来看运行结果:
Client ID in FirstController: qazxsw123456-20160909113523
要注意的一点是,通过provider(name, provider)方法创建服务时必须要显式的实现$get方法,并且只有在$get方法中才能注入其他服务。在AngularJS中服务仅指$get返回的东西,所以前四种创建服务的方法其实都是provider(name, provider)方法根据不同应用场景实现的语法糖,比如factory方法其实就是把一个函数当作了$get方法,service方法其实是将一个函数构造方法或者说函数实例当作了$get方法,value和constant方法其实又是对factory方法的语法糖实现。所以在自定义服务时可按需选择不同的方法创建服务。
版权声明:本文为博主原创文章,未经博主允许不得转载。
: » AngularJS入门教程(三)MVC模式和服务
原创文章,作者:sunnyman218,如若转载,请注明出处:https://blog.ytso.com/251164.html