blob: 6bcd5d2a08dc6d7119adf6fa36c2d65e45cbcfda [file] [log] [blame]
naomib@google.com3fcaf642014-02-13 00:50:10 +00001
Ackermann Yuriy79692902016-04-01 21:41:34 +13002<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "https://www.w3.org/TR/REC-html40/strict.dtd">
naomib@google.com3fcaf642014-02-13 00:50:10 +00003<html>
4<head>
5 <META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
6 <base target="_blank">
naomib@google.com23495712014-02-13 01:07:04 +00007 <link rel="stylesheet" type="text/css" href="styleguide.css">
naomib@google.coma5eb0c52014-02-23 16:10:08 +00008 <script src="https://google-code-prettify.googlecode.com/svn/loader/run_prettify.js"></script>
naomib@google.com3fcaf642014-02-13 00:50:10 +00009 <script language="javascript" src="/eng/doc/devguide/include/styleguide.js"></script>
10 <title>Google's AngularJS Style Guide</title>
11 <style type="text/css"><!--
12 th { background-color: #ddd; }
13 //--></style>
14</head>
15<body onload="prettyPrint();initStyleGuide();">
16<h1 class="external">An AngularJS Style Guide for Closure Users at Google</h1>
17
18<p class="external">This is the external version of a document that was primarily written for Google
19 engineers. It describes a recommended style for AngularJS apps that use Closure, as used
20 internally at Google. Members of the broader AngularJS community should feel free to apply
21 (or not apply) these recommendations, as relevant to their own use cases.</p>
22
23<p class="external">This document describes style for AngularJS apps in google3. This guide
Stephen Hicks8f254422016-11-02 17:17:28 -070024 supplements and extends the <a href="https://google.github.io/styleguide/jsguide.html">
naomib@google.com3fcaf642014-02-13 00:50:10 +000025 Google JavaScript Style Guide</a>.
26</p>
27
28<p><b>Style Note</b>: Examples on the AngularJS external webpage, and many external apps, are
29 written in a style that freely uses closures, favors functional inheritance, and does not often use
30 <a class="external"
Stephen Hicks8f254422016-11-02 17:17:28 -070031 href="https://github.com/google/closure-compiler/wiki/Types-in-the-Closure-Type-System">
naomib@google.com3fcaf642014-02-13 00:50:10 +000032 JavaScript types</a>. Google follows a more rigorous Javascript style to support JSCompiler
33 optimizations and large code bases - see the javascript-style mailing list.
34 This is not an Angular-specific issue, and is not discussed further in this style guide.
35 (But if you want further reading:
36 <a href="http://martinfowler.com/bliki/Lambda.html">Martin Fowler on closures</a>,
37 <a href="http://jibbering.com/faq/notes/closures/">much longer description</a>, appendix A of the
Ackermann Yuriy79692902016-04-01 21:41:34 +130038 <a href="https://books.google.com/books/about/Closure_The_Definitive_Guide.html?id=p7uyWPcVGZsC">
naomib@google.com3fcaf642014-02-13 00:50:10 +000039 closure book</a> has a good description of inheritance patterns and why it prefers
40 pseudoclassical,
Ackermann Yuriy79692902016-04-01 21:41:34 +130041 <a href="https://books.google.com/books/about/JavaScript.html?id=PXa2bby0oQ0C">
naomib@google.com3fcaf642014-02-13 00:50:10 +000042 Javascript, the Good Parts</a> as a counter.)</p>
43
44<h5>1 Angular Language Rules</h5>
45<ul>
46 <li> <a target="_self" href="#googprovide">Manage dependencies with Closure's goog.require and
47 goog.provide</a>
48 <li> <a target="_self" href="#modules"> Modules</a>
49 <li> <a target="_self" href="#moduledeps"> Modules should reference other modules using the
50 "name" property</a>
51 <li> <a target="_self" href="#externs">Use the provided Angular externs file</a>
52 <li> <a target="_self" href="#compilerflags">JSCompiler Flags</a>
53 <li> <a target="_self" href="#controllers">Controllers and Scopes</a>
54 <li> <a target="_self" href="#directives">Directives</a>
55 <li> <a target="_self" href="#services">Services</a>
56</ul>
57<h5>2 Angular Style Rules</h5>
58<ul>
59 <li><a target="_self" href="#dollarsign">Reserve $ for Angular properties and services
60 </a>
61 <li><a target="_self" href="#customelements">Custom elements.</a>
62</ul>
63<h5>3 Angular Tips, Tricks, and Best Practices</h5>
64<ul>
65 <li><a target="_self" href="#testing">Testing</a>
66 <li><a target="_self" href="#appstructure">Consider using the Best Practices for App Structure</a>
67 <li><a target="_self" href="#scopeinheritance">Be aware of how scope inheritance works</a>
68 <li><a target="_self" href="#nginject">Use @ngInject for easy dependency injection compilation</a>
69</ul>
70
71<h5><a target="_self" href="#bestpractices">4 Best practices links and docs</a></h5>
72
73<h2>1 Angular Language Rules</h2>
74
75<h3 id="googprovide">Manage dependencies with Closure's goog.require and goog.provide</h3>
76<p>Choose a namespace for your project, and use goog.provide and goog.require.</p>
77<pre class="prettyprint lang-js">
78goog.provide('hello.about.AboutCtrl');
79goog.provide('hello.versions.Versions');
80</pre>
81
82<p><b>Why?</b>
83 Google BUILD rules integrate nicely with closure provide/require.</p>
84
85<h3 id="modules">Modules</h3>
86
87<p>Your main application module should be in your root client directory. A module should never be
88 altered other than the one where it is defined.</p>
89
90<p>Modules may either be defined in the same file as their components (this works well for a module
91 that contains exactly one service) or in a separate file for wiring pieces together.</p>
92
93<p><b>Why?</b>
94 A module should be consistent for anyone that wants to include it as a reusable component.
95 If a module can mean different things depending on which files are included, it is not consistent.
96</p>
97
98<h3 id="moduledeps">
99 Modules should reference other modules using the Angular Module's "name" property
100</h3>
101
102<p>For example:</p>
103
104<pre class="prettyprint lang-js">
105// file submodulea.js:
106 goog.provide('my.submoduleA');
107
108 my.submoduleA = angular.module('my.submoduleA', []);
109 // ...
110
111// file app.js
112 goog.require('my.submoduleA');
113
114 Yes: my.application.module = angular.module('hello', [my.submoduleA.name]);
115 <font color="red">
116 No: my.application.module = angular.module('hello', ['my.submoduleA']);
117 </font></pre>
118
119<p><b>Why?</b>
120 Using a property of my.submoduleA prevents Closure presubmit failures complaining that the file is
121 required but never used. Using the .name property avoids duplicating strings.</p>
122
naomib@google.com544e0db2014-02-13 21:15:54 +0000123<h3 id="externs">Use a common externs file</h3>
naomib@google.com3fcaf642014-02-13 00:50:10 +0000124
125<p>This maximally allows the JS compiler to enforce type safety in the presence of externally
126 provided types from Angular, and means you don't have to worry about Angular vars being obfuscated
naomib@google.com544e0db2014-02-13 21:15:54 +0000127 in a confusing way. </p>
naomib@google.com3fcaf642014-02-13 00:50:10 +0000128
129<p>Note to readers outside Google: the current externs file is located in an internal-to-Google
130 directory, but an example can be found on github <a href="https://github.com/angular/angular.js/pull/4722">
131 here</a>.</p>
132
133<h3 id="compilerflags">JSCompiler Flags</h3>
134<p><b>Reminder</b>: According to the JS style guide, customer facing code must be compiled.</p>
135
136<p><b>Recommended</b>: Use the JSCompiler (the closure compiler that works with js_binary by
137 default) and ANGULAR_COMPILER_FLAGS_FULL from //javascript/angular/build_defs/build_defs for
138 your base flags.
139</p>
140
141<p>Note - if you are using @export for methods, you will need to add the compiler flag</p>
142<pre>
143"--generate_exports",
144</pre>
145
146<p>If you are using @export for properties, you will need to add the flags:</p>
147<pre>
148"--generate_exports",
149"--remove_unused_prototype_props_in_externs=false",
150"--export_local_property_definitions",
151</pre>
152
153<h3 id="controllers">Controllers and Scopes</h3>
Stephen Hicks8f254422016-11-02 17:17:28 -0700154<p>Controllers are classes. Methods should be defined on MyCtrl.prototype.</p>
naomib@google.com3fcaf642014-02-13 00:50:10 +0000155
156<p>Google Angular applications should use the <b>'controller as'</b> style to export the controller
157 onto the scope. This is fully implemented in Angular 1.2 and can be mimicked in pre-Angular 1.2
158 builds.
159</p>
160
161<p>Pre Angular 1.2, this looks like:</p>
162<pre class="prettyprint lang-js">
163/**
164 * Home controller.
165 *
166 * @param {!angular.Scope} $scope
167 * @constructor
168 * @ngInject
169 * @export
170 */
171hello.mainpage.HomeCtrl = function($scope) {
172 /** @export */
173 $scope.homeCtrl = this; // This is a bridge until Angular 1.2 controller-as
174
175 /**
176 * @type {string}
177 * @export
178 */
179 this.myColor = 'blue';
180};
181
182
183/**
184 * @param {number} a
185 * @param {number} b
186 * @export
187 */
188hello.mainpage.HomeCtrl.prototype.add = function(a, b) {
189 return a + b;
190};
191</pre>
192
193<p>And the template:</p>
194
195<pre>
196&lt;div ng-controller="hello.mainpage.HomeCtrl"/&gt;
197 &lt;span ng-class="homeCtrl.myColor"&gt;I'm in a color!&lt;/span&gt;
198 &lt;span&gt;{{homeCtrl.add(5, 6)}}&lt;/span&gt;
199&lt;/div&gt;
200</pre>
201
202<p>After Angular 1.2, this looks like:</p>
203
204<pre class="prettyprint lang-js">
205/**
206 * Home controller.
207 *
208 * @constructor
209 * @ngInject
210 * @export
211 */
212hello.mainpage.HomeCtrl = function() {
213 /**
214 * @type {string}
215 * @export
216 */
217 this.myColor = 'blue';
218};
219
220
221/**
222 * @param {number} a
223 * @param {number} b
224 * @export
225 */
226hello.mainpage.HomeCtrl.prototype.add = function(a, b) {
227 return a + b;
228};
229</pre>
230
231<p>If you are compiling with property renaming, expose properties and methods using the @export
232 annotation. Remember to @export the constructor as well.</p>
233
234<p>And in the template:</p>
235
236<pre>
237&lt;div ng-controller="hello.mainpage.HomeCtrl as homeCtrl"/&gt;
238 &lt;span ng-class="homeCtrl.myColor"&gt;I'm in a color!&lt;/span&gt;
239 &lt;span&gt;{{homeCtrl.add(5, 6)}}&lt;/span&gt;
240&lt;/div&gt;
241</pre>
242
243<p><b>Why?</b>
244 Putting methods and properties directly onto the controller, instead of building up a scope
245 object, fits better with the Google Closure class style. Additionally, using 'controller as'
246 makes it obvious which controller you are accessing when multiple controllers apply to an element.
247 Since there is always a '.' in the bindings, you don't have to worry about prototypal inheritance
248 masking primitives.</p>
249
250<h3 id="directives">Directives</h3>
251
252<p>All DOM manipulation should be done inside directives. Directives should be kept small and use
253 composition. Files defining directives should goog.provide a static function which returns the
254 directive definition object.</p>
255
256<pre class="prettyprint lang-js">
257goog.provide('hello.pane.paneDirective');
258
259/**
260 * Description and usage
261 * @return {angular.Directive} Directive definition object.
262 */
263hello.pane.paneDirective = function() {
264 // ...
265};
266</pre>
267
268<p><b>Exception</b>: DOM manipulation may occur in services for DOM elements disconnected from the
269 rest of the view, e.g. dialogs or keyboard shortcuts.</p>
270
271<h3 id="services">Services</h3>
272
273<p>Services registered on the module with <code>module.service</code> are classes.
274 Use <code>module.service</code> instead of <code>module.provider</code> or
275 <code>module.factory</code> unless you need to do initialization beyond just creating a
276 new instance of the class.</p>
277
278<pre class="prettyprint lang-js">
279/**
280 * @param {!angular.$http} $http The Angular http service.
281 * @constructor
282 */
283hello.request.Request = function($http) {
284 /** @type {!angular.$http} */
285 this.http_ = $http;
286};
287
288hello.request.Request.prototype.get = function() {/*...*/};
289</pre>
290
291<p>In the module:</p>
292
293<pre class="prettyprint lang-js">
294module.service('request', hello.request.Request);
295</pre>
296
297
298<h2>2 Angular Style Rules</h2>
299
300<h3 id="dollarsign">Reserve $ for Angular properties and services</h3>
301<p>Do not use $ to prepend your own object properties and service identifiers. Consider this style
302 of naming reserved by AngularJS and jQuery.</p>
303
304<p>Yes:</p>
305<pre class="prettyprint lang-js">
306 $scope.myModel = { value: 'foo' }
ralphj@google.com66b9e1b2014-05-21 20:28:07 +0000307 myModule.service('myService', function() { /*...*/ });
naomib@google.com3fcaf642014-02-13 00:50:10 +0000308 var MyCtrl = function($http) {this.http_ = $http;};
309</pre>
310
311<p><font color="red">No:</font></p>
312<pre class="prettyprint">
313 $scope.$myModel = { value: 'foo' } // BAD
314 $scope.myModel = { $value: 'foo' } // BAD
315 myModule.service('$myService', function() { ... }); // BAD
316 var MyCtrl = function($http) {this.$http_ = $http;}; // BAD
317</pre>
318
319<p><b>Why?</b>
320 It's useful to distinguish between Angular / jQuery builtins and things you add yourself.
321 In addition, $ is not an acceptable character for variables names in the JS style guide.
322</p>
323
324<h3 id="customelements">Custom elements</h3>
325
326<p>For custom elements (e.g. <code>&lt;ng-include src="template"&gt;&lt;/ng-include&gt;</code>), IE8
327 requires special support (html5shiv-like hacks) to enable css styling. Be aware of this
328 restriction in apps targeting old versions of IE.</p>
329
330<h2>3 Angular Tips, Tricks, and Best Practices</h2>
331
332<p>These are not strict style guide rules, but are placed here as reference for folks getting
333 started with Angular at Google.</p>
334
335<h3 id="testing">Testing</h3>
336
337<p>Angular is designed for test-driven development.</p>
338
339<p>The recommended unit testing setup is Jasmine + Karma (though you could use closure tests
340 or js_test)</p>
341
342<p>Angular provides easy adapters to load modules and use the injector in Jasmine tests.
343<ul>
Ackermann Yuriy79692902016-04-01 21:41:34 +1300344 <li><a href = "https://docs.angularjs.org/api/angular.mock.module">module</a>
345 <li><a href="https://docs.angularjs.org/api/angular.mock.inject">inject</a>
naomib@google.com3fcaf642014-02-13 00:50:10 +0000346</ul>
347</p>
348
naomib@google.com3fcaf642014-02-13 00:50:10 +0000349
350<h3 id="appstructure">Consider using the Best Practices for App Structure</h3>
naomib@google.comc2482ee2014-02-13 18:27:17 +0000351<p>
352 This <a href="https://docs.google.com/document/d/1XXMvReO8-Awi1EZXAXS4PzDzdNvV6pGcuaF4Q9821Es/pub">directory structure doc</a> describes how to structure your application with controllers in
naomib@google.com3fcaf642014-02-13 00:50:10 +0000353 nested subdirectories and all components (e.g. services and directives) in a 'components' dir.
354</p>
355
naomib@google.com3fcaf642014-02-13 00:50:10 +0000356
357<h3 id="scopeinheritance">Be aware of how scope inheritance works</h3>
358
359<p>See <a href="https://github.com/angular/angular.js/wiki/Understanding-Scopes#wiki-JSproto">
360 The Nuances of Scope Prototypal Inheritance</a></p>
361
362<h3 id="nginject">Use @ngInject for easy dependency injection compilation</h3>
363<p>This removes the need to add <code>myCtrl['$inject'] = ...</code> to prevent minification from
364 messing up Angular's dependency injection.</p>
365
366<p>Usage:</p>
367<pre class="prettyprint lang-js">
368/**
369 * My controller.
370 * @param {!angular.$http} $http
371 * @param {!my.app.myService} myService
372 * @constructor
373 * @export
374 * @ngInject
375 */
376my.app.MyCtrl = function($http, myService) {
377 //...
378};
379</pre>
380
381<h2 id="bestpractices">4 Best practices links and docs</h2>
382
383<ul>
384 <li><a href="https://github.com/angular/angular.js/wiki/Best-Practices">
385 Best Practices</a> from Angular on GitHub</li>
Ackermann Yuriy79692902016-04-01 21:41:34 +1300386 <li><a href="https://www.youtube.com/watch?v=ZhfUv0spHCY">
naomib@google.com3fcaf642014-02-13 00:50:10 +0000387 Meetup video</a> (not Google specific)</li>
388</ul>
389<address>
390 Last modified Feb 07 2013
391</address>
392</body>
393<html>