转: angular编码风格指南
After reading Google's AngularJS guidelines, I felt they were a little too incomplete and also guided towards using the Closure library. They also state "We don't think this makes sense for all projects that use AngularJS, and we'd love to see our community of developers come up with a more general Style that's applicable to AngularJS projects large and small", so here goes.
From my experience with Angular, several talks and working in teams, here's my opinionated styleguide for syntax, building and structuring Angular applications.
Module definitions
Angular modules can be declared in various ways, either stored in a variable or using the getter syntax. Use the getter syntax at all times (angular recommended).
Bad:
var app = angular.module('app', []);
app.controller();
app.factory();
Good:
angular
.module('app', [])
.controller()
.factory();
From these modules we can pass in function references.
Module method functions
Angular modules have a lot of methods, such as controller, factory, directive, service and more. There are many syntaxes for these modules when it comes to dependency injection and formatting your code. Use a named function definition and pass it into the relevant module method, this aids in stack traces as functions aren't anonymous (this could be solved by naming the anonymous function but this method is far cleaner).
Bad:
var app = angular.module('app', []);
app.controller('MyCtrl', function () {
});
Good:
function MainCtrl () {
}
angular
.module('app', [])
.controller('MainCtrl', MainCtrl);
Define a module once using angular.module('app', []) setter, then use the angular.module('app') getter elsewhere (such as other files).
To avoid polluting the global namespace, wrap all your functions during compilation/concatenation inside an IIFE which will produce something like this:
Best:
(function () {
angular.module('app', []);
// MainCtrl.js
function MainCtrl () {
}
angular
.module('app')
.controller('MainCtrl', MainCtrl);
// AnotherCtrl.js
function AnotherCtrl () {
}
angular
.module('app')
.controller('AnotherCtrl', AnotherCtrl);
// and so on...
})();
Controllers
Controllers are classes and can use a controllerAs syntax or generic controller syntax. Use the controllerAs syntax always as it aids in nested scoping and controller instance reference.
controllerAs DOM bindings
Bad:
Good:
Binding these ng-controller attributes couples the declarations tightly with our DOM, and also means we can only use that controller for that specific view (there are rare cases we might use the same view with different controllers). Use the router to couple the controller declarations with the relevant views by telling each route what controller to instantiate.
Best:
This avoids using $parent to access any parent controllers from a child controller, simple hit the main reference and you've got it. This could avoid things such as $parent.$parent calls.
controllerAs this keyword
The controllerAs syntax uses the this keyword inside controllers instead of $scope. When using controllerAs, the controller is infact bound to $scope, there is a degree of separation.
Bad:
function MainCtrl ($scope) {
$scope.someObject = {};
$scope.doSomething = function () {
};
}
angular
.module('app')
.controller('MainCtrl', MainCtrl);
You can also use the prototype Object to create controller classes, but this becomes messy very quickly as each dependency injected provider needs a reference bound to the constructor Object.
Bad and Good:
Good for inheritance, bad (verbose) for general use.
function MainCtrl ($scope) {
this.someObject = {};
this.$scope = $scope;
}
MainCtrl.prototype.doSomething = function () {
// use this.$scope
};
angular
.module('app')
.controller('MainCtrl', MainCtrl);
If you're using prototype and don't know why, then it's bad. If you are using prototype to inherit from other controllers, then that's good. For general use, the prototype pattern can be verbose.
Good:
function MainCtrl () {
this.someObject = {};
this.doSomething = function () {
};
}
angular
.module('app')
.controller('MainCtrl', MainCtrl);
These just show examples of Objects/functions inside Controllers, however we don't want to put logic in controllers...
Avoid controller logic
Avoid writing logic in Controllers, delegate to Factories/Services.
Bad:
function MainCtrl () {
this.doSomething = function () {
};
}
angular
.module('app')
.controller('MainCtrl', MainCtrl);
Good:
function MainCtrl (SomeService) {
this.doSomething = SomeService.doSomething;
}
angular
.module('app')
.controller('MainCtrl', MainCtrl);
This maximises reusability, encapsulated functionality and makes testing far easier and persistent.
Services
Services are instantiated and should be class-like also and reference the this keyword, keep function style consistent with everything else.
Good:
function SomeService () {
this.someMethod = function () {
};
}
angular
.module('app')
.service('SomeService', SomeService);
Factory
Factories give us a singleton module for creating service methods (such as communicating with a server over REST endpoints). Creating and returning a bound Object keeps controller bindings up to date and avoids pitfalls of binding primitive values.
Important: A "factory" is in fact a pattern/implementation, and shouldn't be part of the provider's name. All factories and services should be called "services".
Bad:
function AnotherService () {
var someValue = '';
var someMethod = function () {
};
return {
someValue: someValue,
someMethod: someMethod
};
}
angular
.module('app')
.factory('AnotherService', AnotherService);
Good:
We create an Object with the same name inside the function. This can aid documentation as well for comment-generated docs.
function AnotherService () {
var AnotherService = {};
AnotherService.someValue = '';
AnotherService.someMethod = function () {
};
return AnotherService;
}
angular
.module('app')
.factory('AnotherService', AnotherService);
Any bindings to primitives are kept up to date, and it makes internal module namespacing a little easier, we can easily see any private methods and variables.
Directives
Any DOM manipulation should take place inside a directive, and only directives. Any code reusability should be encapsulated (behavioural and markup related) too.
DOM manipulation
DOM manipulation should be done inside the link method of a directive.
Bad:
// do not use a controller
function MainCtrl (SomeService) {
this.makeActive = function (elem) {
elem.addClass('test');
};
}
angular
.module('app')
.controller('MainCtrl', MainCtrl);
Good:
// use a directive
function SomeDirective (SomeService) {
return {
restrict: 'EA',
template: [
'',
'',
''
].join(''),
link: function ($scope, $element, $attrs) {
// DOM manipulation/events here!
$element.on('click', function () {
$(this).addClass('test');
});
}
};
}
angular
.module('app')
.directive('SomeDirective', SomeDirective);
Any DOM manipulation should take place inside a directive, and only directives. Any code reusability should be encapsulated (behavioural and markup related) too.
Naming conventions
Custom directives should not be ng-* prefixed to prevent future core overrides if your directive name happens to land in Angular (such as when ng-focus landed, there were many custom directives called this beforehand). It also makes it more confusing to know which are core directives and which are custom.
Bad:
function ngFocus (SomeService) {
return {};
}
angular
.module('app')
.directive('ngFocus', ngFocus);
Good:
function focusFire (SomeService) {
return {};
}
angular
.module('app')
.directive('focusFire', focusFire);
Directives are the only providers that we have the first letter as lowercase, this is due to strict naming conventions in the way Angular translates camelCase to hyphenated, so focusFire will become when used on an element.
Usage restriction
If you need to support IE8, you'll want to avoid using the comments syntax for declaring where a directive will sit. Really, this syntax should be avoided anyway - there are no real benefits of using it - it just adds confusion of what is a comment and what isn't.
Bad:
These are terribly confusing.
Good:
Declarative custom elements and attributes are clearest.
You can restrict usage using the restrict property inside each directive's Object. Use E for element, A for attribute, M for comment (avoid) and C for className (avoid this too as it's even more confusing, but plays better with IE). You can have multiple restrictions, such as restrict: 'EA'.
Resolve promises in router, defer controllers
After creating services, we will likely inject them into a controller, call them and bind any new data that comes in. This becomes problematic of keeping controllers tidy and resolving the right data.
Thankfully, using angular-route.js (or a third party such as ui-router.js) we can use a resolve property to resolve the next view's promises before the page is served to us. This means our controllers are instantiated when all data is available, which means zero function calls.
Bad:
function MainCtrl (SomeService) {
var self = this;
// unresolved
self.something;
// resolved asynchronously
SomeService.doSomething().then(function (response) {
self.something = response;
});
}
angular
.module('app')
.controller('MainCtrl', MainCtrl);
Good:
function config ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
resolve: {
doSomething: function (SomeService) {
return SomeService.doSomething();
}
}
});
}
angular
.module('app')
.config(config);
At this point, our service will internally bind the response of the promise to another Object which we can reference in our "deferred-instantiated" controller:
Good:
function MainCtrl (SomeService) {
// resolved!
this.something = SomeService.something;
}
angular
.module('app')
.controller('MainCtrl', MainCtrl);
We can go one better, however and create a resolve property on our own Controllers to couple the resolves with the Controllers and avoid logic in the router.
Best:
// config with resolve pointing to relevant controller
function config ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl',
controllerAs: 'main',
resolve: MainCtrl.resolve
});
}
// controller as usual
function MainCtrl (SomeService) {
// resolved!
this.something = SomeService.something;
}
// create the resolved property
MainCtrl.resolve = {
doSomething: function (SomeService) {
return SomeService.doSomething();
}
};
angular
.module('app')
.controller('MainCtrl', MainCtrl)
.config(config);
Route changes and ajax spinners
While the routes are being resolved we want to show the user something to indicate progress. Angular will fire the $routeChangeStart event as we navigate away from the page, which we can show some form of loading and ajax spinner, which can then be removed on the $routeChangeSuccess event (see docs).
Avoid $scope.$watch
Using $scope.$watch should be avoided unless there are no others options. It's less performant than binding an expression to something like ng-change, a list of supported events are in the Angular docs.
Bad:
Good:
Project/file structure
One role, one file, rule. Separate all controllers, services/factories, directives into individual files. Don't add all controllers in one file, you will end up with a huge file that is very difficult to navigate, keeps things encapsulated and bitesize.
Bad:
|-- app.js
|-- controllers.js
|-- filters.js
|-- services.js
|-- directives.js
Keep naming conventions for files consistent, don't invent fancy names for things, you'll just forget them.
Good:
|-- app.js
|-- controllers/
| |-- MainCtrl.js
| |-- AnotherCtrl.js
|-- filters/
| |-- MainFilter.js
| |-- AnotherFilter.js
|-- services/
| |-- MainService.js
| |-- AnotherService.js
|-- directives/
| |-- MainDirective.js
| |-- AnotherDirective.js
Depending on the size of your code base, a "feature-driven" approach may be better to split into functionality chunks.
Good:
|-- app.js
|-- dashboard/
| |-- DashboardService.js
| |-- DashboardCtrl.js
|-- login/
| |-- LoginService.js
| |-- LoginCtrl.js
|-- inbox/
| |-- InboxService.js
| |-- InboxCtrl.js
Naming conventions and conflicts
Angular provides us many Objects such as $scope and $rootScope that are prefixed with $. This incites they're public and can be used. We also get shipped with things such as $$listeners, which are available on the Object but are considered private methods.
Avoid using $ or $$ when creating your own services/directives/providers/factories.
Bad:
Here we create $$SomeService as the definition, not the function name.
function SomeService () {
}
angular
.module('app')
.factory('$$SomeService', SomeService);
Good:
Here we create SomeService as the definition, and the function name for consistency/stack traces.
function SomeService () {
}
angular
.module('app')
.factory('SomeService', SomeService);
Minification and annotation
Annotation order
It's considered good practice to dependency inject Angular's providers in before our own custom ones.
Bad:
// randomly ordered dependencies
function SomeCtrl (MyService, $scope, AnotherService, $rootScope) {
}
Good:
// ordered Angular -> custom
function SomeCtrl ($scope, $rootScope, MyService, AnotherService) {
}
Minification methods, automate it
Use ng-annotate for automated dependency injection annotation, as ng-min is deprecated. You can find ng-annotate here.
With our function declarations outside of the module references, we need to use the @ngInject comment to explicitly tell ng-annotate where to inject our dependencies. This method uses $inject which is faster than the Array syntax.
Manually specifiying the dependency injection arrays costs too much time.
Bad:
function SomeService ($scope) {
}
// manually declaring is time wasting
SomeService.$inject = ['$scope'];
angular
.module('app')
.factory('SomeService', SomeService);
Good:
Using the ng-annotate keyword @ngInject to instruct things that need annotating:
/**
- @ngInject
*/
function SomeService ($scope) {
}
angular
.module('app')
.factory('SomeService', SomeService);
Will produce:
/**
- @ngInject
*/
function SomeService ($scope) {
}
// automated
SomeService.$inject = ['$scope'];
angular
.module('app')
.factory('SomeService', SomeService);
转: angular编码风格指南的更多相关文章
- R 语言编码风格指南
R 语言是一门主要用于统计计算和绘图的高级编程语言.这份 R 语言编码风格指南旨在让我们的 R代码更容易阅读.分享和检查.以下规则系与 Google 的 R 用户群体协同设计而成. 概要: R编码风格 ...
- 来自 Google 的 R 语言编码风格指南
来自 Google 的 R 语言编码风格指南R 语言是一门主要用于统计计算和绘图的高级编程语言. 这份 R 语言编码风格指南旨在让我们的 R 代码更容易阅读.分享和检查. 以下规则系与 Google ...
- Objective-C 编码风格指南
本文转自:[Objective-C 编码风格指南 | www.samirchen.com][2] ## 背景 保证自己的代码遵循团队统一的编码规范是一个码农的基本节操,能够进入一个有统一编码规范的团队 ...
- Python PEP-8编码风格指南中文版
#PEP 8 – Python编码风格指南 PEP: 8 Title: Style Guide for Python Code Author: Guido van Rossum , Barry War ...
- (转)PEP 8——Python编码风格指南
PEP 8——Python编码风格指南标签(空格分隔): Python PEP8 编码规范原文:https://lizhe2004.gitbooks.io/code-style-guideline-c ...
- PEP8中文版 -- Python编码风格指南
Python部落组织翻译, 禁止转载 目录 缩进 制表符还是空格? 行的最大长度 空行 源文件编码 导入 无法忍受的 其 ...
- 读 Angular 代码风格指南
读 Angular 代码风格指南 本文写于 2021 年 1 月 17 日 原文地址:Angular 文档 该文章拥有完整的代码风格指南--大到如何编排文件夹,小到如何进行变量命名都涉及.但是与 ng ...
- JavaScript编码风格指南(中文版)
前言: 程序语言的编码风格对于一个长期维护的软件非常重要,特别是在团队协作中.如果一个团队使用统一规范的编码分风格,可以提高团队的协作水平和工作效率.编程风格指南的核心是基本的格式化规则,这些规则决定 ...
- Python 编码风格指南
原文:http://python.jobbole.com/84618/ 本文超出 PEP8 的范畴以涵盖我认为优秀的 Python 风格.本文虽然坚持己见,却不偏执.不仅仅涉及语法.模块布局等问题,同 ...
随机推荐
- 摩根斯坦利 - 2016年09月8日 面试题 - HashMap
摩根斯坦利 - 2016年09月8日 面试题: 给定一个 Map<Person, Object> map = new HashMap<Person, Object>(); 放入 ...
- json 的 使用方法以及与数组的区别
JSON简介,全称是JavaScript Object Notation.它是基于JavaScript编程语言ECMA-262 3rd Edition-December 1999标准的一种轻量级的数据 ...
- java 执行linux命令
原文地址: http://blog.csdn.net/xh16319/article/details/17302947 package scut.cs.cwh; import java.io.Inpu ...
- Linux 如何打开端口
举例: 开放10000端口的解决步骤如下: 1.修改/etc/sysconfig/iptables文件,增加如下一行: -A RH-Firewall-1-INPUT -m state --state ...
- 一个Windows C++的线程类实现
Thread.h [cpp] view plaincopy #ifndef __THREAD_H__ #define __THREAD_H__ #include <string> #inc ...
- 分析Ext2文件系统结构。
1. 目的 分析Ext2文件系统结构. 使用 debugfs 应该跟容易分析 Ext2文件系统结构 了解ext2的hole的 2. 准备工作 预习文件系统基本知识: http://www.doc88. ...
- oracle 建表后添加表注释及字段注释
oracle添加表注释和表字段注释 创建Oracle数据库表时候加上注释 CREATE TABLE t1(id varchar2(32) primary key,name VARCHAR2(8) N ...
- Alisha’s Party(队列)
Alisha’s Party Time Limit: 3000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) ...
- Android学习路线(六)为Android应用加入ActionBar
Action bar 是你可以为你的应用的Activity实现的最为重要的设计元素之中的一个.它提供了集中UI特性.而且通过提供和其它的Android应用的一致性体验让你的应用可以非常快被用户熟悉.基 ...
- Root exploit for Android (adb setuid)
/* 本文章由 莫灰灰 编写.转载请注明出处. 作者:莫灰灰 邮箱: minzhenfei@163.com */ 1. 漏洞分析 这是个非常老的漏洞了,主要利用adb启动的时候调用setuid函 ...