AngularJS 常用模块书写建议
2016-03-03 23:39
851 查看
本文是依据 Angular Style Guide 对 Angular 常用模块书写建议的翻译和总结,仅供参考。
使用链式的
建议书写方式:
使用 controllerAs 语法,在把 Controller 和 View 配对 [Style Y038] 时,使用这种方式:
仅在需要使用
虽然类似于下面这种写法很简便,但是那些超过一行代码的函数会降低可读性。
建议书写方式:
不要对多个 Views 使用同一 Controller,如果有可复用代码,应该放到 Factory 中,保持 Controller 专注于它自己的 View [Style Y037]
所有的 Angular Service 都是单例的。
建议将 Factory 中可访问的成员放在顶部 [Style Y052] :
Controller 不需要关心数据是怎么得到的,而只应该知道从谁那里拿数据。
一种建议的 dataservice 书写方式:
当调用一个返回 promise 的 dataservice 时,在调用函数中,也返回一个 promise,方便后续的链式处理 [Style Y061]。 调用 dataservice 的 Controller 的写法如下:
为每个指令单独创建一个文件,这样能方便跨应用共享,并且便于查找 [Style Y070]。还有,为指令提供一个简短唯一的前缀 [Style Y072] 。
将指令限定为 Elements 和 Attributes,这(EA) 在 Angular 1.3+ 中已经是默认设置 [Style Y074] 。
为保持一贯性,在 Directive 中同样应该使用 controllerAs 语法来将 Controller 和 View 配对 [Style Y075] 。由于 Directive 的 Controller 是在 Directive 闭包外面的,所以,如果想将外层 scope 和 Directive 中 Controller 的 scope 绑定,(Angular 1.3+)可以设置
下面是一个完整的示例。
主文件:
有两种方法可以解决这个问题:
方法一
从易于阅读的角度考虑,建议第二种 [Style Y091] 。
当然,如果使用自动化构建工具 Gulp 或 Grunt 的话,还有一种更好的办法,使用 ng-annotate,自动生成
IIFE
使用 立即执行函数表达式(Immediately Invoked Function Expression)将 Angular 组件包裹起来,防止污染全局作用域 Style Y010 。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /* avoid */ // storage.js angular .module('app') .factory('storage', storage); // storage function is added as a global variable function storage() { } /** * recommended * * no globals are left behind */ // storage.js (function() { 'use strict'; angular .module('app') .factory('storage', storage); function storage() { } })(); |
Modules
使用setter定义 Modules ,避免使用变量 [Style Y021]。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /* avoid */ var app = angular.module('app', [ 'ngAnimate', 'ngRoute', 'app.shared', 'app.dashboard' ]); /* recommended */ angular .module('app', [ 'ngAnimate', 'ngRoute', 'app.shared', 'app.dashboard' ]); |
getter来获取 Modules ,避免使用变量 [Style Y022] 。尽量不直接使用匿名函数,而是把一个函数名作为回调传进去 [Style Y024] 。
1 2 3 4 5 | angular .module('app') .controller('SomeController', SomeController); function SomeController() { } |
Controller
使用 controllerAs (和 vm 一起)
因为this是上下文相关的,为了避免 Controlller 内部的函数在使用
this时导致上下文改变,应该在一开始使用一个变量(最好统一为 viewModel 的缩写 -
vm)来捕获
this[Style Y032] 。
建议书写方式:
1 2 3 4 5 | function CustomerController() { var vm = this; vm.name = {}; vm.sendMessage = function() {}; } |
1 2 3 4 56 | function config($routeProvider) { $routeProvider .when('/avengers', { templateUrl: 'avengers.html', controller: 'Avengers', controllerAs: 'vm' }); } |
$emit、
$broadcast、
$on、
$watch等
$scope下的方法,再使用
$scope[Style Y031] ,书写方式如下:
1 2 3 4 56 | function SomeController($scope, $log) { var vm = this; vm.title = 'Some Title'; $scope.$watch('vm.title', function(current, original) { $log.info('vm.title was %s', original); $log.info('vm.title is now %s', current); }); } |
把页面绑定成员放在上面
把可绑定成员放在 Controller 最前面一部分,按字母顺序排列,并且不让代码蔓延。这样能让代码更易读、易查找 [Style Y33] 。虽然类似于下面这种写法很简便,但是那些超过一行代码的函数会降低可读性。
1 2 3 4 56 | /* avoid */ vm.refresh = function() { /** * lines * of * code * affects * readability */ }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 1516 | /* recommended */ function SessionsController() { var vm = this; vm.gotoSession = gotoSession; vm.refresh = sessionDataService.refresh; // 1 liner is OK vm.search = search; vm.sessions = []; vm.title = 'Sessions'; //////////// function gotoSession() { /* */ } function search() { /* */ } } |
其他
将 Controller 中的部分逻辑放在 Service 或 Factory 中,保持 Controller 的简洁 [Style Y035]不要对多个 Views 使用同一 Controller,如果有可复用代码,应该放到 Factory 中,保持 Controller 专注于它自己的 View [Style Y037]
Service && Factory
Service
Angular 中的 Service 会通过new关键字被实例化,其中的方法和属性会被直接添加在
this上。因此,通常可以使用 Factory 代替 Service [Style Y040] 。
所有的 Angular Service 都是单例的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 1516 | // service angular .module('app') .service('logger', logger); function logger() { this.logError = function(msg) { /* */ }; } // factory angular .module('app') .factory('logger', logger); function logger() { return { logError: function(msg) { /* */ } }; } |
Factory
Factory 的创建应该符合单一职责原则 [Style Y050] ,和 Service 一样,Factory 也是单例的,它返回一个包含 Service 中成员的对象,Factory 和 Service 的区别可参见 AngularJS 中 Provider 们 一文。建议将 Factory 中可访问的成员放在顶部 [Style Y052] :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function logger($log) { var service = { error: error, info: info } return service; //////////// function error() { /* */ }; function info() { /* */ }; } |
Data Service
将产生数据和与数据交互的操作放在一个 DataService 的 Factory 中,让其负责 XHR 调用、local storage 等任何与数据相关的操作。这样能让 Controller 专注于展示和为 View 层收集信息上 [Style Y060] 。Controller 不需要关心数据是怎么得到的,而只应该知道从谁那里拿数据。
一种建议的 dataservice 书写方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 2425 | angular .module('app.core') .factory('dataservice', dataservice); dataservice.$inject = ['$http', 'logger']; function dataservice($http, logger) { return { getAvengers: getAvengers }; function getAvengers() { return $http.get('/api/maa') .then(getAvengersComplete) .catch(getAvengersFailed); function getAvengersComplete(response) { return response.data.results; } function getAvengersFailed(error) { logger.error('XHR Failed for getAvengers.' + error.data); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 2425 | angular .module('app.avengers') .controller('AvengersController', AvengersController); AvengersController.$inject = ['dataservice', 'logger']; function AvengersController(dataservice, logger) { var vm = this; vm.avengers = []; activate(); function activate() { return getAvengers().then(function() { logger.info('Activated Avengers View'); }); } function getAvengers() { return dataservice.getAvengers() .then(function(data) { vm.avengers = data; return vm.avengers; }); } } |
Directive
将跟 DOM 相关的操作都放在 Directive 中。在能使用 CSS 设置样式,使用 animation services 设置动画及使用 Angular templating、ngShow或者
ngHide的情况下,尽量避免使用 Directive [Style Y072] 。
为每个指令单独创建一个文件,这样能方便跨应用共享,并且便于查找 [Style Y070]。还有,为指令提供一个简短唯一的前缀 [Style Y072] 。
将指令限定为 Elements 和 Attributes,这(EA) 在 Angular 1.3+ 中已经是默认设置 [Style Y074] 。
为保持一贯性,在 Directive 中同样应该使用 controllerAs 语法来将 Controller 和 View 配对 [Style Y075] 。由于 Directive 的 Controller 是在 Directive 闭包外面的,所以,如果想将外层 scope 和 Directive 中 Controller 的 scope 绑定,(Angular 1.3+)可以设置
bindToController = true[Style Y076]。
下面是一个完整的示例。
主文件:
1 | <div my-example max="77"></div> |
example.directive.js文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 2425 | angular .module('app') .directive('myExample', myExample); function myExample() { var directive = { restrict: 'EA', templateUrl: 'app/feature/example.directive.html', scope: { max: '=' }, controller: ExampleController, controllerAs: 'vm', bindToController: true }; return directive; } function ExampleController() { var vm = this; vm.min = 3; console.log('CTRL: vm.min = %s', vm.min); console.log('CTRL: vm.max = %s', vm.max); } |
example.directive.html文件:
12 | <div>hello world</div> <div>max={{vm.max}}<input ng-model="vm.max"/></div> <div>min={{vm.min}}<input ng-model="vm.min"/></div> |
Dependency Injection
由于AngularJS是通过构造函数的参数名字来推断依赖服务名称的。所以如果要压缩JS代码,它所有的参数也同时会被压缩,这时候依赖注入系统就不能正确的识别出服务了 [Style Y090] 。有两种方法可以解决这个问题:
方法一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 1516 | angular .module('app') .controller('DashboardController', ['$routeParams', 'dataservice', function Dashboard($routeParams, dataservice) {} ]); ``` 方法二 ```javascript angular .module('app') .controller('DashboardController', DashboardController); DashboardController.$inject = ['$routeParams', 'dataservice']; function DashboardController($routeParams, dataservice) { } |
当然,如果使用自动化构建工具 Gulp 或 Grunt 的话,还有一种更好的办法,使用 ng-annotate,自动生成
DashboardController.$inject部分的代码 [Style Y100] ,如下:
1 2 3 4 56 | angular .module('app') .controller('DashboardController', DashboardController); /* @ngInject */ function DashboardController($routeParams, dataservice) { } |
相关文章推荐
- AngularJS中的依赖注入
- 『AngularJS』$location 服务介绍(1)
- angularjs用FormData上传文件
- angularjs表达式中不能直接调用外部的js函数
- AngularJS中的控制器和作用域
- angularjs 和 trim方法冲突
- #学习笔记#(51)angular自定义标签director
- 走进AngularJs(八) ng的路由机制
- Angularjs Ng_repeat中实现复选框选中并显示不同的样式
- 走进AngularJs(中) 服务机制1
- ng-include用法分析以及多标签页面的简单实现方式
- Angularjs轻松实现表格按指定列排序
- 【Angularjs文档翻译及实例】表单指令集(input)
- AngularJs学习记录
- angularjs(1)
- #学习笔记#(50)angular.bind(self,fn,args)
- Angularjs表达式与JavaScript表达式的区别
- bootstrap + angularjs + springmvc + mybatis框架之多个输入框(可为空)组合查询
- bootstrap + angularjs + springmvc + mybatis框架之加载log4j日志
- bootstrap + angularjs + springmvc + mybatis框架之图片上传和展示