按照MVC
模式的设计原则,controller
应该尽可能保持短小精悍。同样,在AngularJS中,不提倡在 controller
中进行DOM
操作和数据操作。先来看一个臃肿的、难以维护的controller
:
var app = angular.module('APPModule', []); app.controller('MainController', function($scope) { $scope.shouldShowLogin = true; $scope.showLogin = function() { $scope.shouldShowLogin =!$scope.shouldShowLogin; }; $scope.clickButton = function() { $('#btnspan').html('Clicked'); }; $scope.onLogin = function(user) { $http({ method: 'POST', url: '/login', data: { user: user } }).success(function(data) { //Some Actions }); }; });
初学者总是倾向于写出这种代码,过多的逻辑和前后台交互被放到了本应用来渲染数据的$scope
对象上。更加优雅的方法是通过使用service
和directive
使controller
更轻量,更易于维护。下面来看看如何自定义并使用service
,至于directive
,我们之后再谈。
1. 创建并使用自定义service
1. 通过factory()
创建自定义service
var app = angular.module('APPModule', []); app.factory('UserService', [ //制定自定义service名为UserService '$http', function($http) { var runLogin = function(user) { $http({ method: 'POST', url: '/login', data: { user: user } }).success(function(data) { //Some Actions }); }; return { runLogin: runLogin }; } ]);
不难发现,如此声明自定义的 service 后,我们将之前臃肿的controller
中的登录逻辑放到了 UserService中,代码马上变得逼格十足。
2. 使用自定义service
app.controller('MainController', [ '$scope', 'UserService', //在此将自定义的UserService注入 function($scope, UserService) { $scope.onLogin = function(user) { //调用UserService的runLogin方法进行登录 UserService.runLogin(user); }; } ]);
这里的UserService就是我们的自定义service
,在controller
初始化时注入,即可使用其中的值和方法了,是不是十分方便?注意一点,在自定义服务之前注入所有的AngularJS内置服务,是约定俗称的规则,比如这里我们就是先注入内置的$scope
,再注入自定义的UserService。
如果自定义的service
和使用它的controller
不在同一个module
中,需要将service
所处的module
注入到controller
所处的module
中:
var serviceModule = angular.module('ServiceModule', []); serviceModule.factory('UserService', [ '$http', function($http) { … //与之前UserService相同 } ]); //将service所处的module注入到controller所处的module中 var app = angular.module('APPModule', ['ServiceModule']); app.controller('MainController', [ '$scope', 'UserService', function($scope, UserService) { … //与之前controller相同 } ]);
2. 创建自定义service的其他方式
factory()
方法是用来创建服务的最常见的方式之一,除此之外,还有一些其他方式可以创建服务。
2.1 使用service()
从service()
这个方法名知道它是用来创建服务的,它允许为服务对象注册一个构造函数:
app.service('ProductService', function($http) { this.getProductList = function() { $http({method: 'GET', url: '/api/product'}); }; });
如果使用和factory()
相同的方式,也可以创建服务:
app.service('ProductService', function($http) { var getProductList = function() { $http({method: 'GET', url: '/api/product'}); }; return { getProductList: getProductList }; });
读者可根据自己的习惯决定具体使用何种方式。
2.2 使用provider()
app.provider('NameService', { self: this, setName: function(name) { self.name = name; }, $get: function() { return { getName: function() { return self.name; } }; } });
使用provider()
创建时,返回的对象中必须有$get()
属性,其值为一个返回共有函数的函数,如果没有$get
属性会报错:
Uncaught Error: [$injector:modulerr] Failed to instantiate module exampleApp due to: Error: [$injector:pget] Provider 'NameService' must define $get factory method.
用provider()
创建服务可以在多个应用使用同一个service
时获得更强的扩展性,通过如下方式对service
进行扩展:
app.config(function(NameServiceProvider) { NameServiceProvider.setName('Lucy'); });
其中,NameServiceProvider为对应的服务名(NameService) + Provider组成,通过这一对象可以调用 NameService中的方法,无论该方法有没有被暴露出来。比如,NameService中只暴露出一个getName
方法,如果直接在controller
中使用NameService.setName('Lucy')会报错:
TypeError: undefined is not a function
但通过provider()
,可以为NameService设初始值。
2.3 使用constant()
准确来说,这里要说到的constant()
和下面要提到的value()
并不算传统意义上MVC
中的service
,为了方便在这一并讲一下。
该方法可以将变量值注册为服务,可以使该变量被其他module
或controller
共享,是一种在多个module
或controller
中共享数据的方法:
app.constant('ASToken', '12345abcde'); //分别对应'key', 'value' app.controller('MainController', [ '$scope', '$log', 'ASToken', //注入constant function($scope, $log, ASToken) { //Some Actions $log.info(ASToken); //12345abcde } ]);
2.4 使用value()
与constant()
用法十分相似,唯一的区别是,constant()
可以注入到config()
中,而value()
不可以:
app.constant('ASToken', '12345').config(function(ASToken) { //正常使用 }); app.constant('ASValue', '12345').config(function(ASValue) { //ASValue在config中无法访问 });
3.AngularJS中的拦截器$provide.decorator():
AngularJS中的$provide
服务提供了在服务实例创建时对其进行拦截的功能,可以在有需求的情况下对服务进行扩展和修改。不仅可以使用在自定义服务上,也可以对AngularJS的内置服务进行拦截。以拦截2.1中provider()
创建的UserService为例:
app.config([ '$provide’, function($provide) { $provide.decorator('UserService', [ '$delegate', function($delegate) { return{ getName: function() { return $delegate.getName() + ' <-- TheFunction Has Been Decorated.' }, getAge: function() { return 18; } }; } ]); } ]);
在config()
中,通过$provide.decorate()
方法拦截UserService,注入的$delegate
可以调用要拦截的service
中原本的方法。这里我们通过对UserService 进行拦截,修改了 UserService中的getName()
方法,并为UserService添加了getAge()
方法。
完。
参考资料:
《AngularJS 权威教程》 作者:Ari Lerner 译者:赵望野 徐飞 何鹏飞
转载于:https://blog.51cto.com/xitongjiagoushi/1659620