与 Java 相似, AngularJS 中的 controller 应该尽可能保持短小精悍,不提倡在 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 }); }; });
coding 初学者总是倾向于写这种代码。更加优雅的方法是通过使用 service 和 directive 使 controller 更轻量,更易于维护。下面来看看如何自定义并使用service 。
1.通过 factory() 创建自定义 service:
var app =angular.module(‘APPModule‘, []); app.factory(‘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‘, function($scope, UserService) { $scope.onLogin = function(user) { UserService.runLogin(user); }; } ]);
这里的 UserService就是我们自定义的 service ,在 controller 初始化时注入,即可使用其中的值和方法了。注意一点,在自定义服务之前注入所有的 AngularJS 内置服务,是约定俗称的规则。
如果自定义的service 和使用它的 controller 不在一个 module中,需要在 controller 所在 module 初始化时注入 service 所在的 module:
var serviceModule = angular.module(‘ServiceModule‘, []); serviceModule.factory(‘UserService‘, [ ‘$http‘, function($http) { … //与之前UserService相同 } ]); //在controller所在的module中注入service所在的module var app =angular.module(‘APPModule‘, [‘ServiceModule‘]); app.controller(‘MainController’, [ ‘$scope‘, ‘UserService‘, function($scope, UserService) { … //与之前controller相同 } ]);
3.创建自定义 service 的其他方式:
factory() 方法是用来创建服务的最常规方式,除此之外,还有其他方式可以创建服务。
3.1 provider():
app.provider(‘UserService‘, { self: this, setName:function(name) { self.name = name; }, $get: function() { return { getName: function() { return self.name; } }; } });
使用provider() 创建时,返回的对象中必须有 $get() 函数对象,否则会创建失败。用 provider() 创建服务可以在多个应用使用同一个服务时获得更强的扩展性,通过如下方式进行扩展:
app.config(function(UserServiceProvider) { NameServiceProvider.setName(‘Lucy‘); });
其中,UserServiceProvider为对应的服务名 + Provider组成,通过这一对象可以调用 UserService中的方法。
3.2 constant():
该方法可以将变量值注册为服务,可以使该变量被其他 module 或 controller 共享:
app.constant(‘ASToken‘, ‘12345‘); app.controller(‘MainController‘, [ ‘$scope‘, ‘ASToken‘, //注入constant function($scope, ASToken) { //Some Actions } ]);
将 constant 注入后就可以直接使用了。
3.3 value():
与 constant()用法相同。与 constant() 的区别是,constant() 可以注入到 config 中,而 value() 不行:
app.constant(‘ASToken‘, ‘12345‘).config(function(ASToken) { //正常使用 }); app.constant(‘ASValue’, ‘12345‘).config(function(ASValue) { //ASValue在config中无法访问 });
4.AngularJS中的拦截器$provide.decorator():
AngularJS 中的 $provide 服务提供了在服务实例创建时对其进行拦截的功能,可以对服务进行扩展和修改。不仅可以使用在自定义服务上,也可以对AngularJS 的内置服务进行拦截。以拦截 3.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 译者:赵望野 徐飞 何鹏飞