Opinionated AngularJS 1.X style guide for teams by @rbholben
The purpose of this style guide is to propose structure and conventions for scalable team-built Angular 1.X CoffeeScript applications. It can also serve as a syntactical "cheat sheet" for how to write various Angular code snippets. This guide has been forked from Todd Motto's AngularJS style guide and customized to work with CoffeeScript. A full list of influencers is in the Acknowledgements section.
The generator-ng-poly
project is a powerful Yeoman generator that comes reasonably close to fitting these conventions. This generator is very flexible, providing a plethora of options (HTML, CSS, JavaScript, Jade, Sass, Less, Stylus, CoffeeScript, et al).
A note about style guides: The goal here is to set a starting point where any team can customize the document to meet their needs. Keep in mind that there are many ways to accomplish the same end goal. Techniques defined below are merely one way. The important thing is that teams agree on their own style early in the process. Since much more time is spent reading code rather than writing code, team members that don't fit in with a common convention can greatly reduce the productivity of the team as a whole.
- Overarching Principles
- General Conventions 1. EditorConfig 1. Linting 1. CoffeeScript
- Angular Code Structure 1. Module Files 1. Angular Method Files 1. Angular Method Names 1. CoffeeScript Class Usage 1. Minification & Annotation 1. Putting it all Together
- Specific Angular Method Conventions 1. Controller 1. Service 1. Directive 1. Filter 1. Config & Run
- File & Folder Conventions
- Tips & Tricks 1. Publish & Subscribe (Pub/Sub) Events 1. Performance 1. Angular Wrapper References
- Acknowledgements
A few basic criteria form a foundation for this style guide:
- Separation of concerns
- Short files, but many of them
- Scalability
- Maximum readability, minimal punctuation
- Declarative purpose at top of files, building details as you go down
- Minimal usage of anonymous functions (for stack trace when debugging)
- DRY (Don't Repeat Yourself)
- Consistency
This section includes broad conventions that are not Angular-specific.
-
An .editorconfig file helps developers define and maintain consistent coding styles between different editors and IDEs.
-
Put the following file at the top level of the project repository so that all contributors are using it.
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
- Adjust as necessary to meet the needs of the team.
-
A linter is a tool that detects errors and potential problems in your code. There are three popular linting tools for JavaScript: JSLint, JSHint, and ESLint. Pick one. I recommend JSHint.
-
Enforce linting by (1) having team members add the appropriate plug-in to their editor, and (2) incorporating the linter into your build system (i.e. Gulp, Grunt, et al).
-
Customize the linting configuration file to meet the needs of the team. For JSHint, a good starting point is this .jshintrc example file.
-
Put the linting configuration file at the top level of the project repository so that all contributors are using it.
-
Parentheses: In CoffeeScript, parentheses are optional in many situations. Favor the approach without parentheses.
# avoid angular.module('someApp')
# recommended angular.module 'someApp'
-
Avoid
this
: Use CoffeeScript's@
instead ofthis
. -
IIFE Scoping: CoffeeScript automatically compiles to JavaScript wrapped inside an IIFE (immediately invoked function expression). This ensures that the global namespace will not be polluted. It is not necessary to add any additional IIFE.
This CoffeeScript...
'use strict' someVar = 'My string!'
compiles into this JavaScript...
(function() { 'use strict' var someVar; someVar = 'My string!'; }).call(this);
A major factor in team productivity is making sure the code inside each file has a consistent look and feel. This section defines that look and feel.
One overarching theme in this guide is that all Angular files should have their module/method info at the top of the file. This theme drives a CoffeeScript class usage pattern throughout.
-
Module Setter & Getters:
angular.module 'someApp', []
sets a module.angular.module 'someApp'
gets the previously set module.A module must be set exactly once. Do this in a standalone file.
# avoid angular.module 'someApp', [ ... ... ] .config... .run...
# recommended angular.module 'someApp', [ ... ... ]
-
Module Philosophy: For anything more than a simple app, consider breaking the app into multiple modules. This does not mean that every widget needs its own module, but it may be sensible to break the app into logical chunks.
One indicator that we might want to break into modules is the list of dependencies in a module setter. If our list of module dependencies is greater than ~5, it might be time. Imagine someone new to the project trying to trace a parameter back to its origin who has to face a list of 20 module dependencies.
Another approach might be to break each page into a module. If each page is complex with many unique types of content (directives, controllers, services), it might make a good module by itself.
Angular apps are built with a handful of primary methods provided by Angular. The frequently used methods are .controller
, .factory
, .service
, .constant
, .value
, .provider
, .directive
, .filter
, config
, and run
. When this guide refers to "Angular methods," we mean these Angular methods.
-
One Angular Method per File: Put exactly one Anguler method per file. This consistent approach will keep our development project more navigable and predictable. Build tools can ultimately concatenate into a single file for production. (See Directives for an exception to this rule).
-
Angular Method Up Top: Place the Angular module and method lines at the top of each file. This makes it immediately evident to the viewer what the code is doing.
-
Angular Method Chaining: Assigning the returned module to a variable is not the preferred approach. Instead, we chain our Angular method onto
angular.module 'someApp'
(the getter). This makes each Angular file consistent and predictable. It reduces the amount of cross-file searching to connect an injected module with a variable.# avoid someApp = angular.module 'someApp', []
# avoid someApp.controller 'SomeCtrl' ...
# recommended angular.module 'someApp', []
# recommended angular.module 'someApp' .controller 'SomeCtrl', ...
Placing the method call on the second line allows us to remove any parentheses on the first line.
-
Angular Method Indentation: Don't bother indenting the Angular method. This is probably your editor's default behavior.
# avoid angular.module 'someApp' .controller 'SomeCtrl', ...
# recommended angular.module 'someApp' .controller 'SomeCtrl', ...
-
Strict Mode: Write all files using strict mode. The first line in each CoffeeScript (or JavaScript) file should be
'use strict'
.
-
lowerCamelCase: Use lowerCamelCase for all directives and filters. Angular requires them to be this way in order to translate into a hyphenated html name, i.e.
someDirective
is translated intosome-directive
.# required angular.module 'someApp' .directive 'someDirective', ...
# required angular.module 'someApp' .filter 'someFilter', ...
-
UpperCamelCase: Use UpperCamelCase for all other Angular methods.
# recommended angular.module 'someApp' .controller 'SomeCtrl', ...
# recommended angular.module 'someApp' .service 'SomeService', ...
# recommended angular.module 'someApp' .constant 'SomeConstant', ...
# recommended angular.module 'someApp' .value 'SomeValue', ...
-
Code Organization: CoffeeScript functions do not support JavaScript function declaration syntax, however CoffeeScript classes do. This nuance allows us to use (or misuse) CoffeeScript classes to organize our code in a readable top-to-bottom way and take advantage of hoisting.
# avoid angular.module 'someApp' .controller 'SomeCtrl', (SomeService) -> ...
# recommended angular.module 'someApp' .controller 'SomeCtrl', class SomeCtrl constructor: (SomeService) -> @publicVar = 'some string' privateVar = 'some string' @publicMethod = (someParam) -> @publicVar = SomeService.someMethod(privateVar) privateMethod(someParam) privateMethod = (someParam) -> privateVar = SomeService.someMethod(someParam)
With this technique, we can use
@
(this
) to explicitly choose which variables are publicly exposed. -
Match Your Names: The string we use to define our Angular method should match the class name that follows it.
# avoid angular.module 'someApp' .controller 'SomeCtrl', class SomethingCool
# recommended angular.module 'someApp' .controller 'SomeCtrl', class SomeCtrl
The exception is for filters where the first letter of the name needs to be lowercase (see Angular Method Names).
# recommended angular.module 'someApp' .filter 'someFilter', class SomeFilter
-
Built-ins First: when pulling parameters into the constructor, list the built-in services before custom services.
# avoid angular.module 'someApp' .service 'SomeService', class SomeService constructor: ($http, $q, SomeOtherService, $log) -> ...
# recommended angular.module 'someApp' .service 'SomeService', class SomeService constructor: ($http, $q, $log, SomeOtherService) -> ...
-
Do not use Angular array syntax or
.$inject
for dependency injection. Neither technique is DRY. They both reduce readability, increase unnecessary code complexity, and increase the potential for mistakes. Let the build system handle this. -
Use ng-annotate for Gulp or Grunt (
ng-min
is deprecated). This will protect the code from minification routines which shorten variable names and break the app. Function declarations will use Angular's.$inject
notation and function expressions will use Angular bracket notation. Adding@ngInject
comments to yur code will explicitly require.$inject
notation which can yield faster performace. -
For situations where JavaScript files are being compressed manually without a build system, make sure to run
ng-annotate
before compressing.
-
Module setter file...
angular.module 'someApp', [ ... ... ]
-
Angular method file (same approach for controller, service, etc.)...
'use strict' angular.module 'someApp' .controller 'SomeCtrl', class SomeCtrl constructor: ($log, someInjectedService) -> @someVar1 = someInjectedService.method1() @someVar2 = someInjectedService.method2()
-
Adding jsDoc comments makes it looks like this...
'use strict' ###* # @name Http # @desc # Send http requests to server. # ### angular.module 'someApp' .service 'Http', class Http constructor: ($http, $q) -> ###* # @name get # @desc Send http get request to API endpoint # @param {String} url - URI to direct request to # @param {String} params - prelim config options # @returns {Object} promise - ... ### @get = (url, params) -> deferred = $q.defer() ... $http(config) .success (data) -> deferred.resolve(data) ...
This section defines a uniform syntax for each of the Angular methods.
-
For Angular methods that require a name string and a function (
.controller
,.service
,.filter
), use the following class/contructor syntax.# recommended for .controller, .service, and .filter angular.module 'someApp' .someMethod 'SomeName', class SomeName constructor: (someInjectedService) -> ...
-
For Angular methods that require a name string and an object (
.directive
,.constant
,.value
), use the following syntax.# recommended for .directive, .constant, and .value angular.module 'someApp' .someMethod 'SomeName', -> ...
-
For Angular methods that require only a function (
.config
,.run
), use the following class/contructor syntax.# recommended for .config and .run angular.module 'someApp' .someMethod class SomeName constructor: (someInjectedService) -> ...
Hint: A
.value
cannot be injected into.config
or.run
. If this becomes necessary, change the.value
to.constant
.
-
Ctrl: Most variables should be spelled out. We make an exception for controllers since they are used everywhere and the word is a bit long. Use "Ctrl".
angular.module 'someApp' .controller 'SomeCtrl', ...
-
controllerAs Syntax: Controllers are classes, so use the
controllerAs
syntax (introduced in Angular 1.1) at all times instead of the original Controller syntax with$scope
. ThecontrollerAs
syntax uses@
(this
) inside controllers, which binds to$scope
.controllerAs
especially shines with nested controllers as it makes all template variables explicitly clear. Using this approach will also make the eventual transition to Angular 2.0 smoother.# avoid this in router ... .when '/home', templateUrl: 'some/path/to/home.html' controller: 'HomeCtrl' ...
# avoid this in controller ... constructor: ($scope) -> $scope.someVar = 'some string' ...
<!-- avoid this in html --> <div> {{ someVar }} </div>
# recommended in router ... .when '/home', templateUrl: 'some/path/to/home.html' controller: 'HomeCtrl' controllerAs: 'home' ...
# recommended in controller ... constructor: -> @someVar = 'some string' ...
<!-- recommended --> <div> {{ home.someVar }} </div>
-
Don't Call Controllers from HTML: For single page apps with a router, controllers should be called from either the router or a directive definition object (DDO), not from an html element. The exception here is the shell page controller or small apps without a router.
<!-- avoid calling controllers from html (except for shell or small apps) --> <div ng-controller="SomeCtrl as sc"> {{ sc.someObject }} </div>
# recommended approach in a directive angular.module 'directives' .directive 'someDirective', -> ... controller: someCtrl controllerAs: 'sc' ...
# recommended approach in a router angular.module 'someApp' .config, class Router constructor: ($routeProvider) -> $routeProvider .when '/home', templateUrl: 'some/path/to/home.html' controller: 'HomeCtrl' controllerAs: 'home' .when '/dashboard', templateUrl: 'some/path/to/dash.html' controller: 'DashCtrl' controllerAs: 'dash' ... .otherwise '/home'
-
Fat Arrow: Use CoffeeScript's "fat arrow" syntax to pass outer scope
this
context into a nested function. Do not bindvm
tothis
when using CoffeeScript.@someVar = (query) => SomeService.someVar(query).then => @arr = SomeService.someObject
Use CoffeeScript's regular arrow if no nested functions.
@someVar = -> SomeService.someObject
-
Avoid
$scope
: Only use$scope
when necessary; for example, publishing and subscribing events using$emit
,$broadcast
,$on
or$watch
. Try to limit the use of these, however, and treat$scope
as a special use case. -
Avoid Business Logic: The only logic in a controller should be presentation logic. Move business logic into a service.
# avoid angular.module 'someApp' .controller 'SomeCtrl', class SomeCtrl constructor: ($http) -> @retrieve = $http.get('/somepath').success (response) => @data = response @delete = someObject, index => $http.delete('/someString/' + someObject.id).then (response) => @data.splice index, 1
# recommended angular.module 'someApp' .controller 'SomeCtrl', class SomeCtrl constructor: (SomeService) -> @retrieve = SomeService.get().then => @data = SomeService.someObject @delete = someObject, index => SomeService.delete(someObject).then => @data.splice index, 1
-
Avoid DOM Manipulation: DOM manipulation should live in a directive.
-
Keep Controllers Clean & Lean: As a rule-of-thumb, try to have an
@
symbol (akin tothis
or$scope
) on at least 50% of the lines in the controller. If not, try to refactor some logic into a separate service.If a controller grows to more than ~100 lines, it might be time to consider breaking the functionality into pieces via custom directives and/or nested scopes.
Another gauge is to look at the code base. If there are more controllers than services, it might be time to refactor.
-
Quick Summary Example:
See code structure explanation for using classes, the constructor, and exposing variables.
'use strict' angular.module 'someApp' .controller 'SomeCtrl', class SomeCtrl constructor: ($log, someInjectedService) -> @someVar1 = someInjectedService.method1() @someVar2 = someInjectedService.method2()
-
Use
.service
Instead of.factory
: All Angular services are singletons. Usage of.service
or.factory
is purely a preference and each provides a different way to create objects. With CoffeeScript, favor services over factories since the syntax exactly matches the way we are defining controllers and filters. -
Five Service Types: Angular has five types of services:
.service
,factory
,constant
,value
, and the all-purposeprovider
. Each of these requires a name string as the first parameter. By not using.factory
, we can follow the same syntax for the remaining three higher level services (service
,constant
, andvalue
). -
Quick Summary Examples:
See code structure explanation for using classes, the constructor, and exposing variables.
angular.module 'someApp' .service 'SomeService', class SomeService constructor: ($http, $q, $log) -> @get = => deferred = $q.defer() $http.get() .success (data) => deferred.resolve(data) @someObject = data .error (data, status) -> $log.error 'Error: ', status, data
angular.module 'someApp' .constant 'SomeConstant', class SomeConstant constructor: -> URL: 'https://api.some_backend.com' CONFIG: headers: 'Some-Backend-Key': '2Opu5BGW416J5jIA1MOKOKHqZEuja4krmNdPbZsh' 'Content-Type': 'application/json'
-
Lean DDO: Keep the top of the directive definition object (DDO) clean and lean. Put controller details in a class further down in the code. For readability, try to keep each property in the DDO at one line, with the exception of template, scope, and link. Since link can be many lines, put link last.
angular.module 'directives' .directive 'someDirective', -> restrict: 'E' templateUrl: 'app/some-directive.html' scope: { someVar: '=' } controller: someCtrl controllerAs: 'sc' link: (scope, elem, attrs) -> ... .controller 'someCtrl', class someCtrl constructor: -> @someVar1 = 'some string' ...
Placing the controller at the bottom of the directive file is an exception to the "One Angular Method per File" rule. If the link function becomes large, it might make sense to move the controller to its own file.
-
Use DDO Syntax ONLY: Angular allows for the creation of directives that return a link function directly as opposed to a DDO. Avoid this syntax as it is not commonly used and may not be familiar to future code readers.
# avoid angular.module 'directives' .directive 'someDirective', class someDirective constructor: (scope, elem, attrs) -> scope.someVar = 'some string' elem.addClass ... ...
# recommended angular.module 'directives' .directive 'someDirective', -> link: (scope, elem, attrs) -> scope.someVar = 'some string' elem.addClass ... ...
-
Declaration Restrictions: Only create element or attribute directives (
restrict: 'E'
orrestrict: 'A'
). Avoid old-style class and comment directives (restrict: 'C'
andrestrict: 'M'
). -
Choosing Directive Type: Simple guideline... If the directive contains a template or equivalent, use an element directive. Otherwise, use an attribute directive to modify the behavior of an existing element. The default value for
restrict
isEA
(since Angular 1.3). This is fine, but keep this guideline in mind coding html.<!-- Element directive when there is a template or equivalent --> <some-directive></some-directive> <!-- Attribute directive when modifying behavior --> <div some-directive></div>
-
Templating: Use
Array.join ''
for a cleaner, easier-to-read template.# avoid template: '<div>' + '<h1>My directive</h1>' + '</div>'
# recommended template: [ '<div>' '<h1>My directive</h1>' '</div>' ].join ''
For longer templates, use
templateUrl
and move to a separate file. This further improves readability and opens up additional editor features (syntax highlighting, emmet, etc.) -
DOM Manipulation: This should only take place inside a directives, not a controller.
# avoid angular.module 'someApp' .controller 'UploadCtrl', class UploadCtrl constructor: -> $('.dragzone').on 'dragend', function -> ...
# recommended angular.module 'someApp' .directive 'dragUpload', -> link: (scope, element, attrs) -> element.on('dragend', -> ...
-
Naming Conventions: Never prefix custom directives with
ng-
, they might conflict with future native directives. It is recommended to prefix all custom directives with company or project-specific characters to reduce the liklihood of naming collisions with 3rd party directives. Also see general name convention comment about lowerCamelCasing directive names. -
controllerAs: Use the
controllerAs
syntax inside directives (same as router). -
Don't Use
replace
: Deprecation warnings started with Angular 1.3.
-
Avoid Array.filter Except in a Filter: Don't use Array.filter in a controller or service. Instead favor its use in an Angular filter so as to enhance testing and reusability.
# avoid angular.module 'someApp' .controller 'SomeCtrl', class SomeCtrl constructor: -> @startsWithLetterA = (items) -> items.filter (item) -> /^a/i.test item.name
# recommended angular.module 'someApp' .filter 'startsWithLetterA', class StartsWithLetterA constructor: -> (items) -> items.filter (item) -> /^a/i.test item.name
-
See general name convention comment about lowerCamelCasing filter names.
Angular's .config
and .run
methods are run once (and only once) at the initial page load. Because they are never called again, they do not have a name string parameter. Thus, the syntax is slightly different.
-
The most common usage for
.config
is probably a router. A router based on the Angular team's "ngView" module should look something like this example.angular.module 'someApp' .config class Router constructor: ($routeProvider) -> $routeProvider .when '/home', templateUrl: 'some/path/to/home.html' controller: 'HomeCtrl' controllerAs: 'home' .when '/dashboard', templateUrl: 'some/path/to/dash.html' controller: 'DashCtrl' controllerAs: 'dash' ... .otherwise '/home'
-
A
.run
script should look something like this.angular.module 'someApp' .run class SomeScript constructor: ($rootScope) -> $rootScope.$on '$routeChangeStart', ... ...
Folder Structure:
This is perhaps the area with more unique opinions than any other. Folder structure can take on many varieties, but most of them can either be described as feature-based or type-based.
A feature-based folder structure is generally preferred simply because it scales in a way that makes it easy for a team member to work in a specific area of the code with all (or most) relevant files in the same folder. A properly designed feature-based folder structure can also guarantee that no folder becomes too unwieldy with more than 5-10 files.
For starting a project, here is a feature-based folder structure that can scale. As the project grows, additional tiers can be added to keep things manageable.
app/
common/
filters/
some_filter1.coffee
some_filter2.coffee
lib_wrappers/ # examples of 3rd party library wrappers
lodash.coffee
moment.coffee
utils/ # examples of general-purpose scripts (not app-specific)
user_auth.coffee
http_wrapper.coffee
utils-module.coffee
directives/ # examples of directives
some_table/
some_table.coffee
some_table.html
some_widget/
...
modules/
app/ # examples of general-purpose, top-level, app-specific scripts
api.coffee
cookies.coffee
routes.coffee
run.coffee
app-module.coffee # this is the script that kicks everything off
crm/
crm-ctrl.coffee
crm.coffee
crm.html
crm-module.coffee
dashboard/
...
dashboard-module.coffee
fonts/
images/
style/
directives/
_some-table.scss
_some-widget.scss
...
modules/
_crm.scss
_dashboard.scss
...
main.scss # register various .scss files with import statements
index.html
Notes about this example structure:
-
Example above shows html, scss, and coffee files. This could just as easily include jade, haml, less, sass, styl, etc.
-
All Sass (
.scss
) files other thanmain.scss
are prefixed with a_
so they can import into themain.scss
file. These files are also placed in their own folder so that their paths don't get disturbed when the folder structure is periodically tweaked. -
A small module file is placed at the same level of the folder that contains the module files. Modules can be put under
modules
orcommon
. -
This file structure is a slightly different than
yo ng-poly
, but ang-poly
starter app can easily be restructured to this convention. -
With a good build system, it is unnecessary to manually add the script paths to the
index.html
file. However, paths to imported.scss
files need to be added tomain.scss
.
File Naming Convention:
-
File names should match the name of the Angular method that lives in it.
-
Convert any camelCase Angular names to snake_case file names, i.e.
angular.directive 'someCoolWidget', ...
translates intosome_cool_widget.coffee
. -
Don't use any UPPER CASE letters in file names.
-
For controller files, add
-ctrl
to the filename. For module files, add-module
to the filename. For all other files, don't add the Angular method to the name. -
Note: Some commonly used file name delimeters are
-
,_
,.
or even camelCasing. The hyphen delimeter works best withyo ng-poly
.
This section is less about style and more about hints and performance recommendations.
-
$scope: Use the
$emit
and$broadcast
methods to trigger events to direct relationship scopes only.# up the $scope $scope.$emit 'customEvent', data # down the $scope $scope.$broadcast 'customEvent', data
-
$rootScope: Use only
$emit
as an application-wide event bus and remember to unbind listeners.# all $rootScope.$on listeners $rootScope.$emit 'customEvent', data
-
Hint: Because the
$rootScope
is never destroyed,$rootScope.$on
listeners aren't either, unlike$scope.$on
listeners and will always persist, so they need destroying when the relevant$scope
fires the$destroy
event.# call the closure unbind = $rootScope.$on 'customEvent' $scope.$on '$destroy', unbind
-
For multiple
$rootScope
listeners, use an Object literal and loop each one on the$destroy
event to unbind all automatically.rootListeners = 'customEvent1': $rootScope.$on 'customEvent1' 'customEvent2': $rootScope.$on 'customEvent2' 'customEvent3': $rootScope.$on 'customEvent3' $scope.$on '$destroy', rootListeners[unbind] for unbind in rootListeners
-
One-time Binding Syntax: Since Angular 1.3, it is possible to use one-time binding syntax
{{ ::value }}
. Binding once removes the watcher from the scope's$$watchers
array after theundefined
variable becomes resolved, thus improving performance in each dirty-check.<!-- avoid --> <h1>{{ sc.title }}</h1> <!-- recommended --> <h1>{{ ::sc.title }}</h1>
-
Consider $scope.$digest: Use
$scope.$digest
over$scope.$apply
where sensible. Only child scopes will update.$scope.$apply
will call$rootScope.$digest
, which causes the entire application$$watchers
to dirty-check again. Using$scope.$digest
will dirty check current and child scopes from the initiated$scope
.
-
3rd Pary Libraries: Wrap any 3rd party libraries (such as Underscore, Lodash, jQuery, Moment, Upload, etc.) in an Angular service to aid testing and Angular references. Details explained in this video.
angular.module 'someApp' .run, class Run # Invoke _ at runtime. Lodash service can now delete the global reference. # No need to do this if 3rd party libraries require window._ to exist. constructor: (_) ->
angular.module 'lib_wrappers' .service '_', class Lodash constructor: -> # Bind a local _ on the global _ _ = window._ # Make sure the global lodash library is no longer available for # accidental usage without proper injection. If other 3rd party # libraries require window._, don't do this! delete window._ ( _ )
-
$document and $window: Use
$document
and$window
at all times to aid testing and Angular references.# avoid dragUpload -> link: scope, elem, attrs -> document.addEventListener 'click', function -> ...
# recommended dragUpload -> link: scope, elem, attrs, $document -> $document.addEventListener 'click', -> ...
-
$timeout and $interval: Use
$timeout
and$interval
over their native counterparts to keep Angular's two-way data binding up to date.# avoid dragUpload -> link: scope, elem, attrs -> setTimeout -> ..., 1000
# recommended dragUpload $timeout -> link: scope, elem, attrs -> $timeout -> ..., 1000
This guide was influenced by the following people and the Angular community as a whole.
- Todd Motto - Opinionated AngularJS styleguide for teams
- John Papa - Angular Style Guide
- Mark Meyer - The Top 10 Mistakes AngularJS Developers Make
- Dustin Specker - Yeoman ng-poly Generator
For anything else, including API reference, check the Angular documentation.
Open an issue first to discuss potential changes/additions.
Copyright (c) 2015 Bob Holben
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.