Giter Club home page Giter Club logo

angularjs-style-guide's Introduction

Join the chat at https://gitter.im/mgechev/angularjs-style-guide

Introduction

The goal of this style guide is to present a set of best practices and style guidelines for one AngularJS application. These best practices are collected from:

  1. AngularJS source code
  2. Source code or articles I've read
  3. My own experience

Note 1: this is still a draft of the style guide, its main goal is to be community-driven so filling the gaps will be greatly appreciated by the whole community.

Note 2: before following any of the guidelines in the translations of the English document, make sure they are up-to date. The latest version of the AngularJS style guide is in the current document.

In this style guide you won't find common guidelines for JavaScript development. Such can be found at:

  1. Google's JavaScript style guide
  2. Mozilla's JavaScript style guide
  3. Douglas Crockford's JavaScript style guide
  4. Airbnb JavaScript style guide
  5. Idiomatic JavaScript style guide

For AngularJS development recommended is the Google's JavaScript style guide.

In AngularJS's GitHub wiki there is a similar section by ProLoser, you can check it here.

Translations

Table of content

General

Directory structure

Since a large AngularJS application has many components it's best to structure it in a directory hierarchy. There are two main approaches:

  • Creating high-level divisions by component types and lower-level divisions by functionality.

In this way the directory structure will look like:

.
β”œβ”€β”€ app
β”‚Β Β  β”œβ”€β”€ app.js
β”‚Β Β  β”œβ”€β”€ controllers
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ home
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ FirstCtrl.js
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── FirstCtrl.spec.js
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── SecondCtrl.js
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── SecondCtrl.spec.js
β”‚Β Β  β”‚Β Β  └── about
β”‚Β Β  β”‚Β Β      └── ThirdCtrl.js
β”‚Β Β  β”‚Β Β      └── ThirdCtrl.spec.js
β”‚Β Β  β”œβ”€β”€ directives
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ home
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── directive1.js
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── directive1.spec.js
β”‚Β Β  β”‚Β Β  └── about
β”‚Β Β  β”‚Β Β      β”œβ”€β”€ directive2.js
β”‚Β Β  β”‚Β Β      β”œβ”€β”€ directive2.spec.js
β”‚Β Β  β”‚Β Β      └── directive3.js
β”‚Β Β  β”‚Β Β      └── directive3.spec.js
β”‚Β Β  β”œβ”€β”€ filters
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ home
β”‚Β Β  β”‚Β Β  └── about
β”‚Β Β  └── services
β”‚Β Β      β”œβ”€β”€ CommonService.js
β”‚Β Β      β”œβ”€β”€ CommonService.spec.js
β”‚Β Β      β”œβ”€β”€ cache
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ Cache1.js
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ Cache1.spec.js
β”‚Β Β      β”‚Β Β  └── Cache2.js
β”‚Β Β      β”‚Β Β  └── Cache2.spec.js
β”‚Β Β      └── models
β”‚Β Β          β”œβ”€β”€ Model1.spec.js
β”‚Β Β          β”œβ”€β”€ Model1.js
β”‚Β Β          └── Model2.spec.js
β”‚Β Β          └── Model2.js
β”œβ”€β”€ partials
β”œβ”€β”€ lib
└── e2e-tests
  • Creating high-level divisions by functionality and lower-level divisions by component types.

Here is its layout:

.
β”œβ”€β”€ app
β”‚Β Β  β”œβ”€β”€ app.js
β”‚Β Β  β”œβ”€β”€ common
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ controllers
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ directives
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ filters
β”‚Β Β  β”‚Β Β  └── services
β”‚Β Β  β”œβ”€β”€ home
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ controllers
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ FirstCtrl.js
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ FirstCtrl.spec.js
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── SecondCtrl.js
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── SecondCtrl.spec.js
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ directives
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── directive1.js
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── directive1.spec.js
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ filters
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ filter1.js
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ filter1.spec.js
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── filter2.js
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── filter2.spec.js
β”‚Β Β  β”‚Β Β  └── services
β”‚Β Β  β”‚Β Β      β”œβ”€β”€ service1.js
β”‚Β Β  β”‚Β Β      β”œβ”€β”€ service1.spec.js
β”‚Β Β  β”‚Β Β      └── service2.js
β”‚Β Β  β”‚Β Β      └── service2.spec.js
β”‚Β Β  └── about
β”‚Β Β      β”œβ”€β”€ controllers
β”‚Β Β      β”‚Β Β  └── ThirdCtrl.js
β”‚Β Β      β”‚Β Β  └── ThirdCtrl.spec.js
β”‚Β Β      β”œβ”€β”€ directives
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ directive2.js
β”‚Β Β      β”‚Β Β  β”œβ”€β”€ directive2.spec.js
β”‚Β Β      β”‚Β Β  └── directive3.js
β”‚Β Β      β”‚Β Β  └── directive3.spec.js
β”‚Β Β      β”œβ”€β”€ filters
β”‚Β Β      β”‚Β Β  └── filter3.js
β”‚Β Β      β”‚Β Β  └── filter3.spec.js
β”‚Β Β      └── services
β”‚Β Β          └── service3.js
β”‚Β Β          └── service3.spec.js
β”œβ”€β”€ partials
β”œβ”€β”€ lib
└── e2e-tests
  • In case the directory name contains multiple words, use lisp-case syntax:
app
 β”œβ”€β”€ app.js
 └── my-complex-module
 Β   Β β”œβ”€β”€ controllers
 Β   Β β”œβ”€β”€ directives
 Β   Β β”œβ”€β”€ filters
 Β    └── services
  • Put all the files associated with the given directive (i.e. templates, CSS/SASS files, JavaScript) in a single folder. If you choose to use this style be consistent and use it everywhere along your project.
app
└── directives
    β”œβ”€β”€ directive1
    β”‚Β Β  β”œβ”€β”€ directive1.html
    β”‚Β Β  β”œβ”€β”€ directive1.js
    β”‚Β Β  β”œβ”€β”€ directive1.spec.js
    β”‚Β Β  └── directive1.sass
    └── directive2
        β”œβ”€β”€ directive2.html
        β”œβ”€β”€ directive2.js
        β”œβ”€β”€ directive2.spec.js
        └── directive2.sass

This approach can be combined with both directory structures above.

  • The unit tests for a given component (*.spec.js) should be located in the directory where the component is. This way when you make changes to a given component finding its test is easy. The tests also act as documentation and show use cases.
services
β”œβ”€β”€ cache
β”‚Β Β  β”œβ”€β”€ cache1.js
β”‚Β Β  └── cache1.spec.js
└── models
    β”œβ”€β”€ model1.js
    └── model1.spec.js
  • The app.js file should contain route definitions, configuration and/or manual bootstrap (if required).
  • Each JavaScript file should only hold a single component. The file should be named with the component's name.
  • Use AngularJS project structure template like Yeoman, ng-boilerplate.

Conventions about component naming can be found in each component section.

Markup

TLDR; Put the scripts at the bottom.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>MyApp</title>
</head>
<body>
  <div ng-app="myApp">
    <div ng-view></div>
  </div>
  <script src="angular.js"></script>
  <script src="app.js"></script>
</body>
</html>

Keep things simple and put AngularJS specific directives after standard attributes. This will make it easier to skim your code and will make it easier to maintain because your attributes are consistently grouped and positioned.

<form class="frm" ng-submit="login.authenticate()">
  <div>
    <input class="ipt" type="text" placeholder="name" require ng-model="user.name">
  </div>
</form>

Other HTML attributes should follow the Code Guide's recommendation

Naming conventions

The following table is shown the naming conventions for every element:

Element Naming style Example usage
Modules lowerCamelCase angularApp
Controllers Functionality + 'Ctrl' AdminCtrl
Directives lowerCamelCase userInfo
Filters lowerCamelCase userFilter
Services UpperCamelCase User constructor
Factories lowerCamelCase dataFactory others

Others

  • Use:
    • $timeout instead of setTimeout
    • $interval instead of setInterval
    • $window instead of window
    • $document instead of document
    • $http instead of $.ajax
    • $location instead of window.location or $window.location
    • $cookies instead of document.cookie

This will make your testing easier and in some cases prevent unexpected behaviour (for example, if you missed $scope.$apply in setTimeout).

  • Automate your workflow using tools like:

  • Use promises ($q) instead of callbacks. It will make your code look more elegant and clean, and save you from callback hell.

  • Use $resource instead of $http when possible. The higher level of abstraction will save you from redundancy.

  • Use an AngularJS pre-minifier (ng-annotate) for preventing problems after minification.

  • Don't use globals. Resolve all dependencies using Dependency Injection, this will prevent bugs and monkey patching when testing.

  • Avoid globals by using Grunt/Gulp to wrap your code in Immediately Invoked Function Expression (IIFE). You can use plugins like grunt-wrap or gulp-wrap for this purpose. Example (using Gulp)

     gulp.src("./src/*.js")
     .pipe(wrap('(function(){\n"use strict";\n<%= contents %>\n})();'))
     .pipe(gulp.dest("./dist"));
  • Do not pollute your $scope. Only add functions and variables that are being used in the templates.

  • Prefer the usage of controllers instead of ngInit. There are only a few appropriate uses of ngInit, such as for aliasing special properties of ngRepeat, and for injecting data via server side scripting. Besides these few cases, you should use controllers rather than ngInit to initialize values on a scope. The expression passed to ngInit should go through lexing, parsing and evaluation by the Angular interpreter implemented inside the $parse service. This leads to:

    • Performance impact, because the interpreter is implemented in JavaScript
    • The caching of the parsed expressions inside the $parse service doesn't make a lot of sense in most cases, since ngInit expressions are often evaluated only once
    • Is error-prone, since you're writing strings inside your templates, there's no syntax highlighting and further support by your editor
    • No run-time errors are thrown
  • Do not use $ prefix for the names of variables, properties and methods. This prefix is reserved for AngularJS usage.

  • Do not use JQUERY inside your app, If you must, use JQLite instead with angular.element.

  • When resolving dependencies through the DI mechanism of AngularJS, sort the dependencies by their type - the built-in AngularJS dependencies should be first, followed by your custom ones:

module.factory('Service', function ($rootScope, $timeout, MyCustomDependency1, MyCustomDependency2) {
  return {
    //Something
  };
});

Modules

  • Modules should be named with lowerCamelCase. For indicating that module b is submodule of module a you can nest them by using namespacing like: a.b.

    There are two common ways for structuring the modules:

    1. By functionality
    2. By component type

    Currently there's not a big difference, but the first way looks cleaner. Also, if lazy-loading modules is implemented (currently not in the AngularJS roadmap), it will improve the app's performance.

Controllers

  • Do not manipulate DOM in your controllers, this will make your controllers harder for testing and will violate the Separation of Concerns principle. Use directives instead.

  • The naming of the controller is done using the controller's functionality (for example shopping cart, homepage, admin panel) and the substring Ctrl in the end.

  • Controllers are plain javascript constructors, so they will be named UpperCamelCase (HomePageCtrl, ShoppingCartCtrl, AdminPanelCtrl, etc.).

  • The controllers should not be defined as globals (even though AngularJS allows this, it is a bad practice to pollute the global namespace).

  • Use the following syntax for defining controllers:

    function MyCtrl(dependency1, dependency2, ..., dependencyn) {
      // ...
    }
    module.controller('MyCtrl', MyCtrl);

    In order to prevent problems with minification, you can automatically generate the array definition syntax from the standard one using tools like ng-annotate (and grunt task grunt-ng-annotate).

    Another alternative will be to use $inject like:

    angular
     .module('app')
     .controller('HomepageCtrl', HomepageCtrl);
    
    HomepageCtrl.$inject = ['$log', '$http', 'ngRoute'];
    
    function HomepageCtrl($log, $http, ngRoute) {
     // ...
    }
  • Avoid use of $scope service to define functions and properties as part of controllers. Use $scope only if It's really needed: 0. For publish and subscribe to events: $scope.$emit, $scope.$broadcast, and $scope.$on. 0. For watch values or collections: $scope.$watch, $scope.$watchCollection

  • Prefer using controller as syntax and capture this using a variable:

    <div ng-controller="MainCtrl as main">
       {{ main.things }}
    </div>
    app.controller('MainCtrl', MainCtrl);
    MainCtrl.$inject = ['$http'];
    
    function MainCtrl ($http) {
      var vm = this;
      //a clearer visual connection on how is defined on the view
      vm.title = 'Some title';
      vm.description = 'Some description';
    
      $http.get('/api/main/things').then(function (response) {
          vm.things = response.data.things; // Adding 'things' as a property of the controller
      });
    }

    Avoid using this keyword repeatedly inside a controller:

      app.controller('MainCtrl', MainCtrl);
      MainCtrl.$inject = ['$http'];
    
      // Avoid
      function MainCtrl ($http) {
        this.title = 'Some title';
        this.description = 'Some description';
    
        $http.get('/api/main/things').then(function (response) {
            // Warning! 'this' is in a different context here.
            // The property will not be added as part of the controller context
            this.things = response.data.things;
        });
      }

    Using a consistent and short variable name is preferred, for example vm.

    The main benefits of using this syntax:

    • Creates an 'isolated' component - binded properties are not part of $scope prototype chain. This is good practice since $scope prototype inheritance has some major drawbacks (this is probably the reason it was removed on Angular 2):
      • It is hard to track where data is coming from.
      • Scope's value changes can affect places you did not intend to affect.
      • Harder to refactor.
      • The 'dot rule'.
    • Removes the use of $scope when no need for special operations (as mentioned above). This is a good preparation for AngularJS V2.
    • Syntax is closer to that of a 'vanilla' JavaScript constructor

    Digging more into controller as: digging-into-angulars-controller-as-syntax

  • If using array definition syntax, use the original names of the controller's dependencies. This will help you produce more readable code:

    function MyCtrl(l, h) {
      // ...
    }
    
    module.controller('MyCtrl', ['$log', '$http', MyCtrl]);

    which is less readable than:

    function MyCtrl($log, $http) {
      // ...
    }
    
    module.controller('MyCtrl', ['$log', '$http', MyCtrl]);

    This especially applies to a file that has so much code that you'd need to scroll through. This would possibly cause you to forget which variable is tied to which dependency.

  • Make the controllers as lean as possible. Abstract commonly used functions into a service.

  • Avoid writing business logic inside controllers. Delegate business logic to a model, using a service. For example:

    //This is a common behaviour (bad example) of using business logic inside a controller.
    angular.module('Store', [])
    .controller('OrderCtrl', function () {
      var vm = this;
    
      vm.items = [];
    
      vm.addToOrder = function (item) {
        vm.items.push(item);//-->Business logic inside controller
      };
    
      vm.removeFromOrder = function (item) {
        vm.items.splice(vm.items.indexOf(item), 1);//-->Business logic inside controller
      };
    
      vm.totalPrice = function () {
        return vm.items.reduce(function (memo, item) {
          return memo + (item.qty * item.price);//-->Business logic inside controller
        }, 0);
      };
    });

    When delegating business logic into a 'model' service, controller will look like this (see 'use services as your Model' for service-model implementation):

    // order is used as a 'model'
    angular.module('Store', [])
    .controller('OrderCtrl', function (order) {
      var vm = this;
    
      vm.items = order.items;
    
      vm.addToOrder = function (item) {
        order.addToOrder(item);
      };
    
      vm.removeFromOrder = function (item) {
        order.removeFromOrder(item);
      };
    
      vm.totalPrice = function () {
        return order.total();
      };
    });

    Why business logic / app state inside controllers is bad?

    • Controllers instantiated for each view and dies when the view unloads
    • Controllers are not reusable - they are coupled with the view
    • Controllers are not meant to be injected
  • Communicate within different controllers using method invocation (possible when a child wants to communicate with its parent) or $emit, $broadcast and $on methods. The emitted and broadcasted messages should be kept to a minimum.

  • Make a list of all messages which are passed using $emit, $broadcast and manage it carefully because of name collisions and possible bugs.

    Example:

    // app.js
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    Custom events:
      - 'authorization-message' - description of the message
        - { user, role, action } - data format
          - user - a string, which contains the username
          - role - an ID of the role the user has
          - action - specific action the user tries to perform
    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  • When you need to format data encapsulate the formatting logic into a filter and declare it as dependency:

    function myFormat() {
      return function () {
        // ...
      };
    }
    module.filter('myFormat', myFormat);
    
    function MyCtrl($scope, myFormatFilter) {
      // ...
    }
    
    module.controller('MyCtrl', MyCtrl);
  • In case of nested controllers use "nested scoping" (the controllerAs syntax):

    app.js

    module.config(function ($routeProvider) {
      $routeProvider
        .when('/route', {
          templateUrl: 'partials/template.html',
          controller: 'HomeCtrl',
          controllerAs: 'home'
        });
    });

    HomeCtrl

    function HomeCtrl() {
      var vm = this;
    
      vm.bindingValue = 42;
    }

    template.html

    <div ng-bind="home.bindingValue"></div>

Directives

  • Name your directives with lowerCamelCase.
  • Use scope instead of $scope in your link function. In the compile, post/pre link functions you have already defined arguments which will be passed when the function is invoked, you won't be able to change them using DI. This style is also used in AngularJS's source code.
  • Use custom prefixes for your directives to prevent name collisions with third-party libraries.
  • Do not use ng or ui prefixes since they are reserved for AngularJS and AngularJS UI usage.
  • DOM manipulations must be done only through directives.
  • Create an isolated scope when you develop reusable components.
  • Use directives as attributes or elements instead of comments or classes, this will make your code more readable.
  • Use scope.$on('$destroy', fn) for cleaning up. This is especially useful when you're wrapping third-party plugins as directives.
  • Do not forget to use $sce when you should deal with untrusted content.

Filters

  • Name your filters with lowerCamelCase.
  • Make your filters as light as possible. They are called often during the $digest loop so creating a slow filter will slow down your app.
  • Do a single thing in your filters, keep them coherent. More complex manipulations can be achieved by piping existing filters.

Services

This section includes information about the service component in AngularJS. It is not dependent of the way of definition (i.e. as provider, .factory, .service), except if explicitly mentioned.

  • Use camelCase to name your services.

    • UpperCamelCase (PascalCase) for naming your services, used as constructor functions i.e.:

      function MainCtrl(User) {
          var vm = this;
          vm.user = new User('foo', 42);
      }
      
      module.controller('MainCtrl', MainCtrl);
      
      function User(name, age) {
        this.name = name;
        this.age = age;
      }
      
      module.factory('User', function () {
        return User;
      });
    • lowerCamelCase for all other services.

  • Encapsulate all the business logic in services. Prefer using it as your model. For example:

    // order is the 'model'
    angular.module('Store')
    .factory('order', function () {
        var add = function (item) {
          this.items.push (item);
        };
    
        var remove = function (item) {
          if (this.items.indexOf(item) > -1) {
            this.items.splice(this.items.indexOf(item), 1);
          }
        };
    
        var total = function () {
          return this.items.reduce(function (memo, item) {
            return memo + (item.qty * item.price);
          }, 0);
        };
    
        return {
          items: [],
          addToOrder: add,
          removeFromOrder: remove,
          totalPrice: total
        };
    });

    See 'Avoid writing business logic inside controllers' for an example of a controller consuming this service.

  • Services representing the domain preferably a service instead of a factory. In this way we can take advantage of the "klassical" inheritance easier:

     function Human() {
       //body
     }
     Human.prototype.talk = function () {
       return "I'm talking";
     };
    
     function Developer() {
       //body
     }
     Developer.prototype = Object.create(Human.prototype);
     Developer.prototype.code = function () {
       return "I'm coding";
     };
    
     myModule.service('human', Human);
     myModule.service('developer', Developer);
  • For session-level cache you can use $cacheFactory. This should be used to cache results from requests or heavy computations.

  • If given service requires configuration define the service as provider and configure it in the config callback like:

     angular.module('demo', [])
     .config(function ($provide) {
       $provide.provider('sample', function () {
         var foo = 42;
         return {
           setFoo: function (f) {
             foo = f;
           },
           $get: function () {
             return {
               foo: foo
             };
           }
         };
       });
     });
    
     var demo = angular.module('demo');
    
     demo.config(function (sampleProvider) {
       sampleProvider.setFoo(41);
     });

Templates

  • Use ng-bind or ng-cloak instead of simple {{ }} to prevent flashing content.
  • Avoid writing complex expressions in the templates.
  • When you need to set the src of an image dynamically use ng-src instead of src with {{ }} template.
  • When you need to set the href of an anchor tag dynamically use ng-href instead of href with {{ }} template.
  • Instead of using scope variable as string and using it with style attribute with {{ }}, use the directive ng-style with object-like parameters and scope variables as values:
    <div ng-controller="MainCtrl as main">
        <div ng-style="main.divStyle">my beautifully styled div which will work in IE</div>;
    </div>
  angular
    .module('app')
    .controller('MainCtrl', MainCtrl);

  MainCtrl.$inject = [];

  function MainCtrl() {
    var vm = this;
    vm.divStyle = {
        width: 200,
        position: 'relative'
    };
  }

Routing

  • Use resolve to resolve dependencies before the view is shown.
  • Do not place explicit RESTful calls inside the resolve callback. Isolate all the requests inside appropriate services. This way you can enable caching and follow the separation of concerns principle.

E2E Testing

E2E tests are the next common sense step after unit tests, that will allow you to trace bugs and errors in the behaviour of your system. They are great for providing a sanity check that most common scenarios of using your application works. This way you can automate the process and run it each time before you deploy your application.

Ideally, Angular End-to-End tests are written in Jasmine. These tests are run using the Protractor E2E test runner which uses native events and has special features for Angular applications.

File structure:

.
β”œβ”€β”€ app
β”‚Β Β  β”œβ”€β”€ app.js
β”‚Β Β  β”œβ”€β”€ home
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ home.html
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ controllers
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ FirstCtrl.js
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ FirstCtrl.spec.js
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ directives
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── directive1.js
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── directive1.spec.js
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ filters
β”‚Β Β  β”‚Β Β  β”‚Β Β  β”œβ”€β”€ filter1.js
β”‚Β Β  β”‚Β Β  β”‚Β Β  └── filter1.spec.js
β”‚Β Β  β”‚Β Β  └── services
β”‚Β Β  β”‚Β Β      β”œβ”€β”€ service1.js
β”‚Β Β  β”‚Β Β      └── service1.spec.js
β”‚Β Β  └── about
β”‚Β Β      β”œβ”€β”€ about.html
β”‚Β Β      β”œβ”€β”€ controllers
β”‚Β Β      β”‚Β Β  └── ThirdCtrl.js
β”‚Β Β      β”‚Β Β  └── ThirdCtrl.spec.js
β”‚Β Β      └── directives
β”‚Β Β       Β Β  β”œβ”€β”€ directive2.js
β”‚Β Β       Β Β  └── directive2.spec.js
β”œβ”€β”€ partials
β”œβ”€β”€ lib
└── e2e-tests
    β”œβ”€β”€ protractor.conf.js
    └── specs
        β”œβ”€β”€ home.js
        └── about.js

i18n

  • For newer versions of the framework (>=1.4.0) use the built-in i18n tools, when using older versions (<1.4.0) use angular-translate.

Performance

  • Optimize the digest cycle

    • Watch only the most vital variables. When required to invoke the $digest loop explicitly (it should happen only in exceptional cases), invoke it only when required (for example: when using real-time communication, don't cause a $digest loop in each received message).
    • For content that is initialized only once and then never changed, use single-time watchers like bindonce for older versions of AngularJS or one-time bindings in AngularJS >=1.3.0.
       <div>
         {{ ::main.things }}
       </div>
      or
         <div ng-bind="::main.things"></div>
      After that, no watchers will be created for main.things and any changes of main.things will not update the view.
    • Make the computations in $watch as simple as possible. Making heavy and slow computations in a single $watch will slow down the whole application (the $digest loop is done in a single thread because of the single-threaded nature of JavaScript).
    • When watching collections, do not watch them deeply when not strongly required. Better use $watchCollection, which performs a shallow check for equality of the result of the watched expression and the previous value of the expression's evaluation.
    • Set third parameter in $timeout function to false to skip the $digest loop when no watched variables are impacted by the invocation of the $timeout callback function.
    • When dealing with big collections, which change rarely, use immutable data structures.
  • Consider decreasing number of network requests by bundling/caching html template files into your main javascript file, using grunt-html2js / gulp-html2js. See here and here for details. This is particularly useful when the project has a lot of small html templates that can be a part of the main (minified and gzipped) javascript file.

Contribution

Since the goal of this style guide is to be community-driven, contributions are greatly appreciated. For example, you can contribute by extending the Testing section or by translating the style guide to your language.

Contributors

mgechev morizotter chatii2412 pascalockert yanivefraim ericguirbal
mgechev morizotter chatii2412 pascalockert yanivefraim ericguirbal
agnislav ray7551 mainyaa LeonardCModoran elfinxx tiagobarreto
agnislav ray7551 mainyaa LeonardCModoran elfinxx tiagobarreto
Xuefeng-Zhu SullyP giacomocusinato rubystream lukaszklis Spuffynism
Xuefeng-Zhu SullyP giacomocusinato rubystream lukaszklis Spuffynism
susieyy cironunes cavarzan guiltry MSafter mingchen
susieyy cironunes cavarzan guiltry MSafter mingchen
jmblog luixaviles andreasonny83 kuzzmi jabhishek adambabik
jmblog luixaviles andreasonny83 kuzzmi jabhishek adambabik
astalker clbn atodorov apetro valgreens bitdeli-chef
astalker clbn atodorov apetro valgreens bitdeli-chef
meetbryce unseen1980 cminhho dwmkerr kuzmeig1 dominickolbe
meetbryce unseen1980 cminhho dwmkerr kuzmeig1 dominickolbe
gsamokovarov grvcoelho yassirh bargaorobalo hermankan jesselpalmer
gsamokovarov grvcoelho yassirh bargaorobalo hermankan jesselpalmer
capaj johnnyghost jordanyee whoan nacyot mariolamacchia
capaj johnnyghost jordanyee whoan nacyot mariolamacchia
mischkl michaelmov kirstein mo-gr mortonfox cryptojuice
mischkl michaelmov kirstein mo-gr mortonfox cryptojuice
nktssh olafahn olov vorktanamobay QuietHeartThinkingFar raphaelfruneaux
nktssh olafahn olov vorktanamobay QuietHeartThinkingFar raphaelfruneaux
sahat ganchiku kaneshin imaimiami dooart thomastuts
sahat ganchiku kaneshin imaimiami dooart thomastuts
UrielMiranda vkarampinis grapswiz coderhaoxin giantray ntaoo
UrielMiranda vkarampinis grapswiz coderhaoxin giantray ntaoo
seyyah dchest
seyyah dchest

angularjs-style-guide's People

Contributors

agnislav avatar andreasonny83 avatar cavarzan avatar cironunes avatar elfinxx avatar ericguirbal avatar giacomocusinato avatar guiltry avatar jmblog avatar leonardcmodoran avatar luixaviles avatar lukaszklis avatar mgechev avatar mingchen avatar morizotter avatar msafter avatar pascalockert avatar pengfeiwang321 avatar raphaelfruneaux avatar rubystream avatar sahat avatar spuffynism avatar star8ks avatar sullyp avatar susieyy avatar tiagobarreto avatar tornad avatar vorktanamobay avatar xuefeng-zhu avatar yanivefraim avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

angularjs-style-guide's Issues

Coding style

There is no information about coding style -e.g.

  • indenting - spaces (2,4) vs tabs
  • Keywords and True/False/Null (lower, upper, camelize)?
  • Properties naming (camel case?)
  • Control Structures (where to put braces and newlines)
  • functions (and parameters in fucntions space between each?)

I think that guys from php-fig are doing really good job in standarizing those things in PHP community: PSR-2 coding standard. http://www.php-fig.org/psr/psr-2/

I think that there could be some reference to https://github.com/airbnb/javascript - it look like very popular >7000 stars on GH

there may be a mispell

In order to prevent problems with minification, you can automatically generate the array definition syntax from the standard one using tools like ng-annotate (and grunt task grunt-ng-annotate).

Another alternative will be to use $inject like:

angular
.module('app')
.controller('HomepageCtrl', Homepage);

HomepageCtrl.$inject = ['$log', '$http', 'ngRoute'];

function HomepageCtrl($log, $http, ngRoute) {
// ...
}

Put unit tests with the code they test

Instead of putting unit tests to their own subtree, put them with the files they test.

For example:

* services
  * cache
    * cache1.js
    * cache1.test.js
  * models
    * model1.js
    * model1.test.js

This way when we're updating code it's clear that there is a test that will need to be updated first. The tests also act as documentation and show use cases.

I think you'll find tests to be more well-respected here instead of neglected or an afterthought.

Add license

What are the license terms for this guide? Or are they just proprietary/default copyright?

It would be nice if the terms for the guide made it free and open source. AngularJS itself uses the MIT license, so this might be a good choice.

The license terms should probably go into a LICENSE(.md) file at the root of the repository. They could also be briefly mentioned in the README file.

Intended level of concern

The style guide so far deals with issues other than pure angular related. The most prominent example is a recommendation for the line length.

Is this style guide intended to go to that level? If that is the case, than it should also state things about indentation, white spaces, usage of var foo = function () {} vs function foo() {} etc. Maybe, it should declare a jshint/jslint configuration and recommend code to pass without errors?

If the style guide is more intended to deal with "higher level" style regarding angular, I would drop the paragraph Source Code Layout.

Elaborate on service instead of a factory

Not sure if an issue is the right place to raise these questions, but there doesn't seem to be any other discussion forum, so...

Could you elaborate on the point:

Services encapsulating business logic are preferably a service instead of a factory

I've been favouring factory as it lets me specify an explicit API to return, and is very similar to the more generic JavaScript Module Pattern. Ultimately there is little difference other than how they are constructed, so I'm not sure why I should be using service over factory.

scope.state - value or function/service?

For state things that are changing like logged in state or the current tab to highlight, what's the best way to make those values accessible in the scope?

For instance:

  • Use values such as scope.isLoggedIn and scope.currentTab. Set those values using scope.watch or after services run that may change the value.
  • Use functions or services attached to the scope such as scope.isLoggedIn() and scope.currentTab() which return values based values from other scrope variables or values within the controller or service closures.

Thanks!

Recommendations for module constant usage?

Hi, this is great guidelines. One thing that i haven't found is recommendation for constants.
How should i name them? lowerCase or PascalCase? And regarding their structure i have two possibilities in mind:

1st: the all-together
myModule.constant('MyModuleConstants', {
  features: {
    featA: 'featA',
    'featB: 'featB'
  },
  options: {
    optA: 'optA',
    optB: 'optB'
  }
});
2nd: the split
myModule.constant('MyModuleConstantFeatures', {
  featA: 'featA',
  'featB: 'featB'
});
myModule.constant('MyModuleConstantOptions', {
  optA: 'optA',
  optB: 'optB'
});

Any opinions? Thanks.

"Services" section

  • Is the "Services" section implicitly inclusive of value, factory, and provider? If so, it seems confusing to use one of the terms to refer to all 4 collectively. Perhaps the heading should call out all 4. If anything, we should use "Provider" since that's what all 4 factor down to in Angular.
  • The wording in the Services section is unclear. Are we basically saying that services specifically (as opposed to factories, for example) should be UpperCamelCase since they're "newed"? Seems inconsistent since the service as it's consumed is an instance, not a class.
  • Is "klassical" an intentional misspelling here?

Where do HTML files go for controllers?

With the alternative below, where do the HTML pages go?
I assume they go in the controllers and directives folders?

β”œβ”€β”€ app
β”‚ β”œβ”€β”€ app.js
β”‚ β”œβ”€β”€ common
β”‚ β”‚ β”œβ”€β”€ controllers
β”‚ β”‚ β”œβ”€β”€ directives
β”‚ β”‚ β”œβ”€β”€ filters
β”‚ β”‚ └── services
β”‚ β”œβ”€β”€ page1
β”‚ β”‚ β”œβ”€β”€ controllers
β”‚ β”‚ β”‚ β”œβ”€β”€ FirstCtrl.js
β”‚ β”‚ β”‚ └── SecondCtrl.js
β”‚ β”‚ β”œβ”€β”€ directives
β”‚ β”‚ β”‚ └── directive1.js
β”‚ β”‚ β”œβ”€β”€ filters
β”‚ β”‚ β”‚ β”œβ”€β”€ filter1.js
β”‚ β”‚ β”‚ └── filter2.js
β”‚ β”‚ └── services
β”‚ β”‚ β”œβ”€β”€ service1.js
β”‚ β”‚ └── service2.js
β”‚ └── page2
β”‚ β”œβ”€β”€ controllers
β”‚ β”‚ └── ThirdCtrl.js
β”‚ β”œβ”€β”€ directives
β”‚ β”‚ β”œβ”€β”€ directive2.js
β”‚ β”‚ └── directive3.js
β”‚ β”œβ”€β”€ filters
β”‚ β”‚ └── filter3.js
β”‚ └── services
β”‚ └── service3.js
β”œβ”€β”€ lib
└── test

Style guide plugin

Hello all,

Do we have a plugin or some tool through which I can apply formation on code which follows this guide lines? If my code is not proper then it shows me an error/notification for the same.?

services giving too many options on naming...

Why are services given the option of camelCase and PascalCase... The moment I read this it just sounds like "do what ever you want who cares about having any style guide"...

To be honest I don't know if this is mentioned anywhere else because I almost feel like this guide isn't worth following now... (poor excuse to not read the rest, I know)

Also can we stop with the confusing/bad terminology of 'camelCase (lower or upper)' and 'lowerCamelCase', I don't know where the industry came up with the idea that camelCase should have two variants but it needs to stop.

Always including templates / partials along-side other code?

Further to the point of including HTML, CSS, and test files in the same package for directives, why not do the same thing for controllers? We've been doing it and find it helps the development flow quite a bit. When we run our build scripts (gulp-ngtemplate, htmlhint, etc.) it's easy enough to just target .html files, so we haven't seen the need for a separate partials directory.

Feature - add a 'Tests' section

Add "tests" section, containing testing best practices/patterns.
Again, I would love do contribute to a section like this.

Migration to Angular 2.0

What about adding style guidelines that are focused on preparing for Angular 2.0.

I saw this amazing video from ngVegas and read this cool article, which let me think that we should really focus on preparing our apps so it will be much simpler to migrate to Angular 2 when the time comes.

What do you think? Is there a place for a section like this in the guide?

Fix the project structure

Each single page application has a single page. In this case creating different directories called: page1, page2, etc. is inappropriate. The idea behind this organization is to put each different view into a different directory.

More information right here.

Naming services

Hi,

I have a bit confusion about how to name services.

The Services section says:

UpperCamelCase (PascalCase) for naming your services, used as constructor functions.

...

lowerCamelCase for all other services.

However, the Order factory example uses UpperCamelCase but it is not used as constructor function (the injector injects an Order object). Shouldn't it use lowerCamelCase?

Thanks.

Different terminology in "Services" section

Hi,

I'm wondering about the terminology used in these points in "Services" section:

  • Encapsulate the business logic in models created using services.
  • For creating models it is preferred to use service instead of factory.

I'm not convinced that model is a proper word. From my experience it's reserved for properties of an Angular Scope object.

From AngularJS docs:

In Angular, a model is any data that is reachable as a property of an angular Scope object.

Objects created using service or factory are just services I guess.

Put views with the controllers, directives

Instead of putting views in their own subtree, put them with the files that use them.

For example:

* pages 
  * page1
    * page1.ctrl.js
    * page1.test.js
    * page1.html
    * page1.sass
  * page2
    * page2.ctrl.js
    * page2.test.js
    * page2.html
    * page2.sass
* directives 
  * directive1
    * directive1.js
    * directive1.test.js
    * directive1.html
    * directive1.sass
  * directive2
    * directive2.js
    * directive2.test.js
    * directive2.html
    * directive2.sass

This way when we're updating code it's clear how everything ties together.

Angular Directory Structure - Addition of Modules

It seems more intuitive to have a modules directory that contains all modules similar to how MeanJS handles it. source

.
β”œβ”€β”€ app
β”‚   β”œβ”€β”€ app.js
β”‚   β”œβ”€β”€ common
β”‚   β”‚   β”œβ”€β”€ controllers
β”‚   β”‚   β”œβ”€β”€ directives
β”‚   β”‚   β”œβ”€β”€ filters
β”‚   β”‚   └── services
β”‚   β”œβ”€β”€ home
β”‚   β”‚   β”œβ”€β”€ controllers
β”‚   β”‚   β”‚   β”œβ”€β”€ FirstCtrl.js
β”‚   β”‚   β”‚   └── SecondCtrl.js
β”‚   β”‚   β”œβ”€β”€ directives
β”‚   β”‚   β”‚   └── directive1.js
β”‚   β”‚   β”œβ”€β”€ filters
β”‚   β”‚   β”‚   β”œβ”€β”€ filter1.js
β”‚   β”‚   β”‚   └── filter2.js
β”‚   β”‚   └── services
β”‚   β”‚       β”œβ”€β”€ service1.js
β”‚   β”‚       └── service2.js
β”‚   └── about
β”‚       β”œβ”€β”€ controllers
β”‚       β”‚   └── ThirdCtrl.js
β”‚       β”œβ”€β”€ directives
β”‚       β”‚   β”œβ”€β”€ directive2.js
β”‚       β”‚   └── directive3.js
β”‚       β”œβ”€β”€ filters
β”‚       β”‚   └── filter3.js
β”‚       └── services
β”‚           └── service3.js
β”œβ”€β”€ partials
β”œβ”€β”€ lib
└── test

Naming conventions for directories with multiple words

How do you recommend I treat a module folder with multiple words in it? For example, Build & Price. Should it be buildAndPrice or build-and-price. The first looks funny, so we've been using the lower-cased dash-separated conventions for folders.

Also, it would probably be helpful if the sample module code had a multi-word directory in it.

services and scope

Do you have a suggestion for services that need to read and modify the scope?

For example:

  • Pass in the scope and change it in the service.
  • Pass in the scope and use the output of the service to change the scope in the controller.
  • Extract out data needed for the service from the scope and only pass that data to the service instead of the whole scope, then update the scope in the controller using the results of the service.

Thanks!

Please clarify the wording

Hi guys,

I'm updating a russian translation and bumped into a question with the following sentence.

Services representing the domain preferably a service instead of a factory.

Could you please describe the meaning of the "Services representing the domain" phrase? I understand an example but to keep a consistency I need to understand this in details.

Thanks a lot!

How to avoid β€œpolluting” the resolve method with ui-router

I am looking for a solution/ best practices for this problem with Angular routing using ui-router

http://stackoverflow.com/questions/22254954/how-to-avoid-polluting-the-resolve-method-with-ui-router

Basically I have many methods to resolve, some of them quite long, and it looks like the only solution is to place them in factories and just reference them from the router.

In terms of app structure, place all these factories under services > routing for example?

Any recommendation?ο»Ώ

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.