| |
| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "https://www.w3.org/TR/REC-html40/strict.dtd"> |
| <html> |
| <head> |
| <META http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> |
| <base target="_blank"> |
| <link href="/styleguide/favicon.ico" type="image/x-icon" rel="shortcut icon"> |
| <link rel="stylesheet" type="text/css" href="styleguide.css"> |
| <script src="https://google-code-prettify.googlecode.com/svn/loader/run_prettify.js"></script> |
| <script language="javascript" src="/eng/doc/devguide/include/styleguide.js"></script> |
| <title>Google's AngularJS Style Guide</title> |
| <style type="text/css"><!-- |
| th { background-color: #ddd; } |
| //--></style> |
| </head> |
| <body onload="prettyPrint();initStyleGuide();"> |
| <h1 class="external">An AngularJS Style Guide for Closure Users at Google</h1> |
| |
| <p class="external">This is the external version of a document that was primarily written for Google |
| engineers. It describes a recommended style for AngularJS apps that use Closure, as used |
| internally at Google. Members of the broader AngularJS community should feel free to apply |
| (or not apply) these recommendations, as relevant to their own use cases.</p> |
| |
| <p class="external">This document describes style for AngularJS apps in google3. This guide |
| supplements and extends the <a href="https://google.github.io/styleguide/jsguide.html"> |
| Google JavaScript Style Guide</a>. |
| </p> |
| |
| <p><b>Style Note</b>: Examples on the AngularJS external webpage, and many external apps, are |
| written in a style that freely uses closures, favors functional inheritance, and does not often use |
| <a class="external" |
| href="https://github.com/google/closure-compiler/wiki/Types-in-the-Closure-Type-System"> |
| JavaScript types</a>. Google follows a more rigorous Javascript style to support JSCompiler |
| optimizations and large code bases - see the javascript-style mailing list. |
| This is not an Angular-specific issue, and is not discussed further in this style guide. |
| (But if you want further reading: |
| <a href="http://martinfowler.com/bliki/Lambda.html">Martin Fowler on closures</a>, |
| <a href="http://jibbering.com/faq/notes/closures/">much longer description</a>, appendix A of the |
| <a href="https://books.google.com/books/about/Closure_The_Definitive_Guide.html?id=p7uyWPcVGZsC"> |
| closure book</a> has a good description of inheritance patterns and why it prefers |
| pseudoclassical, |
| <a href="https://books.google.com/books/about/JavaScript.html?id=PXa2bby0oQ0C"> |
| Javascript, the Good Parts</a> as a counter.)</p> |
| |
| <h5>1 Angular Language Rules</h5> |
| <ul> |
| <li> <a target="_self" href="#googprovide">Manage dependencies with Closure's goog.require and |
| goog.provide</a> |
| <li> <a target="_self" href="#modules"> Modules</a> |
| <li> <a target="_self" href="#moduledeps"> Modules should reference other modules using the |
| "name" property</a> |
| <li> <a target="_self" href="#externs">Use the provided Angular externs file</a> |
| <li> <a target="_self" href="#compilerflags">JSCompiler Flags</a> |
| <li> <a target="_self" href="#controllers">Controllers and Scopes</a> |
| <li> <a target="_self" href="#directives">Directives</a> |
| <li> <a target="_self" href="#services">Services</a> |
| </ul> |
| <h5>2 Angular Style Rules</h5> |
| <ul> |
| <li><a target="_self" href="#dollarsign">Reserve $ for Angular properties and services |
| </a> |
| <li><a target="_self" href="#customelements">Custom elements.</a> |
| </ul> |
| <h5>3 Angular Tips, Tricks, and Best Practices</h5> |
| <ul> |
| <li><a target="_self" href="#testing">Testing</a> |
| <li><a target="_self" href="#appstructure">Consider using the Best Practices for App Structure</a> |
| <li><a target="_self" href="#scopeinheritance">Be aware of how scope inheritance works</a> |
| <li><a target="_self" href="#nginject">Use @ngInject for easy dependency injection compilation</a> |
| </ul> |
| |
| <h5><a target="_self" href="#bestpractices">4 Best practices links and docs</a></h5> |
| |
| <h2>1 Angular Language Rules</h2> |
| |
| <h3 id="googprovide">Manage dependencies with Closure's goog.require and goog.provide</h3> |
| <p>Choose a namespace for your project, and use goog.provide and goog.require.</p> |
| <pre class="prettyprint lang-js"> |
| goog.provide('hello.about.AboutCtrl'); |
| goog.provide('hello.versions.Versions'); |
| </pre> |
| |
| <p><b>Why?</b> |
| Google BUILD rules integrate nicely with closure provide/require.</p> |
| |
| <h3 id="modules">Modules</h3> |
| |
| <p>Your main application module should be in your root client directory. A module should never be |
| altered other than the one where it is defined.</p> |
| |
| <p>Modules may either be defined in the same file as their components (this works well for a module |
| that contains exactly one service) or in a separate file for wiring pieces together.</p> |
| |
| <p><b>Why?</b> |
| A module should be consistent for anyone that wants to include it as a reusable component. |
| If a module can mean different things depending on which files are included, it is not consistent. |
| </p> |
| |
| <h3 id="moduledeps"> |
| Modules should reference other modules using the Angular Module's "name" property |
| </h3> |
| |
| <p>For example:</p> |
| |
| <pre class="prettyprint lang-js"> |
| // file submodulea.js: |
| goog.provide('my.submoduleA'); |
| |
| my.submoduleA = angular.module('my.submoduleA', []); |
| // ... |
| |
| // file app.js |
| goog.require('my.submoduleA'); |
| |
| Yes: my.application.module = angular.module('hello', [my.submoduleA.name]); |
| <font color="red"> |
| No: my.application.module = angular.module('hello', ['my.submoduleA']); |
| </font></pre> |
| |
| <p><b>Why?</b> |
| Using a property of my.submoduleA prevents Closure presubmit failures complaining that the file is |
| required but never used. Using the .name property avoids duplicating strings.</p> |
| |
| <h3 id="externs">Use a common externs file</h3> |
| |
| <p>This maximally allows the JS compiler to enforce type safety in the presence of externally |
| provided types from Angular, and means you don't have to worry about Angular vars being obfuscated |
| in a confusing way. </p> |
| |
| <p>Note to readers outside Google: the current externs file is located in an internal-to-Google |
| directory, but an example can be found on github <a href="https://github.com/angular/angular.js/pull/4722"> |
| here</a>.</p> |
| |
| <h3 id="compilerflags">JSCompiler Flags</h3> |
| <p><b>Reminder</b>: According to the JS style guide, customer facing code must be compiled.</p> |
| |
| <p><b>Recommended</b>: Use the JSCompiler (the closure compiler that works with js_binary by |
| default) and ANGULAR_COMPILER_FLAGS_FULL from //javascript/angular/build_defs/build_defs for |
| your base flags. |
| </p> |
| |
| <p>Note - if you are using @export for methods, you will need to add the compiler flag</p> |
| <pre> |
| "--generate_exports", |
| </pre> |
| |
| <p>If you are using @export for properties, you will need to add the flags:</p> |
| <pre> |
| "--generate_exports", |
| "--remove_unused_prototype_props_in_externs=false", |
| "--export_local_property_definitions", |
| </pre> |
| |
| <h3 id="controllers">Controllers and Scopes</h3> |
| <p>Controllers are classes. Methods should be defined on MyCtrl.prototype.</p> |
| |
| <p>Google Angular applications should use the <b>'controller as'</b> style to export the controller |
| onto the scope. This is fully implemented in Angular 1.2 and can be mimicked in pre-Angular 1.2 |
| builds. |
| </p> |
| |
| <p>Pre Angular 1.2, this looks like:</p> |
| <pre class="prettyprint lang-js"> |
| /** |
| * Home controller. |
| * |
| * @param {!angular.Scope} $scope |
| * @constructor |
| * @ngInject |
| * @export |
| */ |
| hello.mainpage.HomeCtrl = function($scope) { |
| /** @export */ |
| $scope.homeCtrl = this; // This is a bridge until Angular 1.2 controller-as |
| |
| /** |
| * @type {string} |
| * @export |
| */ |
| this.myColor = 'blue'; |
| }; |
| |
| |
| /** |
| * @param {number} a |
| * @param {number} b |
| * @export |
| */ |
| hello.mainpage.HomeCtrl.prototype.add = function(a, b) { |
| return a + b; |
| }; |
| </pre> |
| |
| <p>And the template:</p> |
| |
| <pre> |
| <div ng-controller="hello.mainpage.HomeCtrl"/> |
| <span ng-class="homeCtrl.myColor">I'm in a color!</span> |
| <span>{{homeCtrl.add(5, 6)}}</span> |
| </div> |
| </pre> |
| |
| <p>After Angular 1.2, this looks like:</p> |
| |
| <pre class="prettyprint lang-js"> |
| /** |
| * Home controller. |
| * |
| * @constructor |
| * @ngInject |
| * @export |
| */ |
| hello.mainpage.HomeCtrl = function() { |
| /** |
| * @type {string} |
| * @export |
| */ |
| this.myColor = 'blue'; |
| }; |
| |
| |
| /** |
| * @param {number} a |
| * @param {number} b |
| * @export |
| */ |
| hello.mainpage.HomeCtrl.prototype.add = function(a, b) { |
| return a + b; |
| }; |
| </pre> |
| |
| <p>If you are compiling with property renaming, expose properties and methods using the @export |
| annotation. Remember to @export the constructor as well.</p> |
| |
| <p>And in the template:</p> |
| |
| <pre> |
| <div ng-controller="hello.mainpage.HomeCtrl as homeCtrl"/> |
| <span ng-class="homeCtrl.myColor">I'm in a color!</span> |
| <span>{{homeCtrl.add(5, 6)}}</span> |
| </div> |
| </pre> |
| |
| <p><b>Why?</b> |
| Putting methods and properties directly onto the controller, instead of building up a scope |
| object, fits better with the Google Closure class style. Additionally, using 'controller as' |
| makes it obvious which controller you are accessing when multiple controllers apply to an element. |
| Since there is always a '.' in the bindings, you don't have to worry about prototypal inheritance |
| masking primitives.</p> |
| |
| <h3 id="directives">Directives</h3> |
| |
| <p>All DOM manipulation should be done inside directives. Directives should be kept small and use |
| composition. Files defining directives should goog.provide a static function which returns the |
| directive definition object.</p> |
| |
| <pre class="prettyprint lang-js"> |
| goog.provide('hello.pane.paneDirective'); |
| |
| /** |
| * Description and usage |
| * @return {angular.Directive} Directive definition object. |
| */ |
| hello.pane.paneDirective = function() { |
| // ... |
| }; |
| </pre> |
| |
| <p><b>Exception</b>: DOM manipulation may occur in services for DOM elements disconnected from the |
| rest of the view, e.g. dialogs or keyboard shortcuts.</p> |
| |
| <h3 id="services">Services</h3> |
| |
| <p>Services registered on the module with <code>module.service</code> are classes. |
| Use <code>module.service</code> instead of <code>module.provider</code> or |
| <code>module.factory</code> unless you need to do initialization beyond just creating a |
| new instance of the class.</p> |
| |
| <pre class="prettyprint lang-js"> |
| /** |
| * @param {!angular.$http} $http The Angular http service. |
| * @constructor |
| */ |
| hello.request.Request = function($http) { |
| /** @type {!angular.$http} */ |
| this.http_ = $http; |
| }; |
| |
| hello.request.Request.prototype.get = function() {/*...*/}; |
| </pre> |
| |
| <p>In the module:</p> |
| |
| <pre class="prettyprint lang-js"> |
| module.service('request', hello.request.Request); |
| </pre> |
| |
| |
| <h2>2 Angular Style Rules</h2> |
| |
| <h3 id="dollarsign">Reserve $ for Angular properties and services</h3> |
| <p>Do not use $ to prepend your own object properties and service identifiers. Consider this style |
| of naming reserved by AngularJS and jQuery.</p> |
| |
| <p>Yes:</p> |
| <pre class="prettyprint lang-js"> |
| $scope.myModel = { value: 'foo' } |
| myModule.service('myService', function() { /*...*/ }); |
| var MyCtrl = function($http) {this.http_ = $http;}; |
| </pre> |
| |
| <p><font color="red">No:</font></p> |
| <pre class="prettyprint"> |
| $scope.$myModel = { value: 'foo' } // BAD |
| $scope.myModel = { $value: 'foo' } // BAD |
| myModule.service('$myService', function() { ... }); // BAD |
| var MyCtrl = function($http) {this.$http_ = $http;}; // BAD |
| </pre> |
| |
| <p><b>Why?</b> |
| It's useful to distinguish between Angular / jQuery builtins and things you add yourself. |
| In addition, $ is not an acceptable character for variables names in the JS style guide. |
| </p> |
| |
| <h3 id="customelements">Custom elements</h3> |
| |
| <p>For custom elements (e.g. <code><ng-include src="template"></ng-include></code>), IE8 |
| requires special support (html5shiv-like hacks) to enable css styling. Be aware of this |
| restriction in apps targeting old versions of IE.</p> |
| |
| <h2>3 Angular Tips, Tricks, and Best Practices</h2> |
| |
| <p>These are not strict style guide rules, but are placed here as reference for folks getting |
| started with Angular at Google.</p> |
| |
| <h3 id="testing">Testing</h3> |
| |
| <p>Angular is designed for test-driven development.</p> |
| |
| <p>The recommended unit testing setup is Jasmine + Karma (though you could use closure tests |
| or js_test)</p> |
| |
| <p>Angular provides easy adapters to load modules and use the injector in Jasmine tests. |
| <ul> |
| <li><a href = "https://docs.angularjs.org/api/angular.mock.module">module</a> |
| <li><a href="https://docs.angularjs.org/api/angular.mock.inject">inject</a> |
| </ul> |
| </p> |
| |
| |
| <h3 id="appstructure">Consider using the Best Practices for App Structure</h3> |
| <p> |
| 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 |
| nested subdirectories and all components (e.g. services and directives) in a 'components' dir. |
| </p> |
| |
| |
| <h3 id="scopeinheritance">Be aware of how scope inheritance works</h3> |
| |
| <p>See <a href="https://github.com/angular/angular.js/wiki/Understanding-Scopes#wiki-JSproto"> |
| The Nuances of Scope Prototypal Inheritance</a></p> |
| |
| <h3 id="nginject">Use @ngInject for easy dependency injection compilation</h3> |
| <p>This removes the need to add <code>myCtrl['$inject'] = ...</code> to prevent minification from |
| messing up Angular's dependency injection.</p> |
| |
| <p>Usage:</p> |
| <pre class="prettyprint lang-js"> |
| /** |
| * My controller. |
| * @param {!angular.$http} $http |
| * @param {!my.app.myService} myService |
| * @constructor |
| * @export |
| * @ngInject |
| */ |
| my.app.MyCtrl = function($http, myService) { |
| //... |
| }; |
| </pre> |
| |
| <h2 id="bestpractices">4 Best practices links and docs</h2> |
| |
| <ul> |
| <li><a href="https://github.com/angular/angular.js/wiki/Best-Practices"> |
| Best Practices</a> from Angular on GitHub</li> |
| <li><a href="https://www.youtube.com/watch?v=ZhfUv0spHCY"> |
| Meetup video</a> (not Google specific)</li> |
| </ul> |
| <address> |
| Last modified Feb 07 2013 |
| </address> |
| </body> |
| <html> |