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:

{{ someObject }}

Good:

{{ main.someObject }}

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:

{{ main.someObject }}

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编码风格指南的更多相关文章

  1. R 语言编码风格指南

    R 语言是一门主要用于统计计算和绘图的高级编程语言.这份 R 语言编码风格指南旨在让我们的 R代码更容易阅读.分享和检查.以下规则系与 Google 的 R 用户群体协同设计而成. 概要: R编码风格 ...

  2. 来自 Google 的 R 语言编码风格指南

    来自 Google 的 R 语言编码风格指南R 语言是一门主要用于统计计算和绘图的高级编程语言. 这份 R 语言编码风格指南旨在让我们的 R 代码更容易阅读.分享和检查. 以下规则系与 Google ...

  3. Objective-C 编码风格指南

    本文转自:[Objective-C 编码风格指南 | www.samirchen.com][2] ## 背景 保证自己的代码遵循团队统一的编码规范是一个码农的基本节操,能够进入一个有统一编码规范的团队 ...

  4. Python PEP-8编码风格指南中文版

    #PEP 8 – Python编码风格指南 PEP: 8 Title: Style Guide for Python Code Author: Guido van Rossum , Barry War ...

  5. (转)PEP 8——Python编码风格指南

    PEP 8——Python编码风格指南标签(空格分隔): Python PEP8 编码规范原文:https://lizhe2004.gitbooks.io/code-style-guideline-c ...

  6. PEP8中文版 -- Python编码风格指南

    Python部落组织翻译, 禁止转载 目录      缩进      制表符还是空格?      行的最大长度      空行      源文件编码      导入      无法忍受的      其 ...

  7. 读 Angular 代码风格指南

    读 Angular 代码风格指南 本文写于 2021 年 1 月 17 日 原文地址:Angular 文档 该文章拥有完整的代码风格指南--大到如何编排文件夹,小到如何进行变量命名都涉及.但是与 ng ...

  8. JavaScript编码风格指南(中文版)

    前言: 程序语言的编码风格对于一个长期维护的软件非常重要,特别是在团队协作中.如果一个团队使用统一规范的编码分风格,可以提高团队的协作水平和工作效率.编程风格指南的核心是基本的格式化规则,这些规则决定 ...

  9. Python 编码风格指南

    原文:http://python.jobbole.com/84618/ 本文超出 PEP8 的范畴以涵盖我认为优秀的 Python 风格.本文虽然坚持己见,却不偏执.不仅仅涉及语法.模块布局等问题,同 ...

随机推荐

  1. static和extern关键字 对函数的作用

    本文目录 • 一.extern与函数 • 二.static与函数 • 三.static.extern与函数的总结说明:这个C语言专题,是学习iOS开发的前奏.也为了让有面向对象语言开发经验的程序员,能 ...

  2. leetcode Valid Sudoku python

    #数独(すうどく,Sūdoku)是一种运用纸.笔进行演算的逻辑游戏.玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行.每一列.每一个粗线宫内的数字均含1-9,不重复.#数独盘 ...

  3. Docker镜像与仓库(一)

    Docker镜像与仓库(一) Docker镜像与仓库(一) 如何查找镜像? Docker Hub https://registry.hub.docker.com docker search [OPTI ...

  4. 编写一个程序实现strcat函数的功能

    写自己的strcat函数------→mycat #include <stdio.h> #include <string.h> #define N 5 char *mycat( ...

  5. zabbix 的安装

    第一步:官方的源: rpm -ivh http://repo.zabbix.com/zabbix/2.4/rhel/7/x86_64/zabbix-release-2.4-1.el7.noarch.r ...

  6. sql server 2012 镜像和出现的问题

    镜像安装的环境: 主机:win server 2012 , sql server 2012 ,ip:192.168.1.189  PC账户:administrator 备机:win server 20 ...

  7. 十度好友问题(DFS经典应用)

    问题: 在社交网络里(比如 LinkedIn),如果A和B是好友,B和C是好友,但是A和C不是好友,那么C是A的二度好友,给定一个社交网络的关系图,如何找到某一个人的所有十度好友.

  8. 使用hibernate 分表做增删改查

    公司项目有一张表的数据量特别大.而且时间越长累积的数据量就越大. 后来DBA决定分表来解决性能问题. 分表是指   一个母体表  一群子表(结构和字段与母体表完全一样) 我们程序对母表操作其实就是对子 ...

  9. Largest Rectangle in a Histogram(最大矩形面积,动态规划思想)

    Largest Rectangle in a Histogram Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 ...

  10. 加入功能区buttonRibbon Button到SP2010特定列表或库

    加入功能区button到SP2010某一列表或库         有时候你须要给列表/库的功能区加入新button--没有什么比这更简单的了. 你仅仅须要新建一个SP项目.加入一个feature,加入 ...