GithubHelp home page GithubHelp logo

emmanueldemey / eslint-plugin-angular Goto Github PK

View Code? Open in Web Editor NEW
618.0 17.0 131.0 1.5 MB

ESLint plugin for AngularJS applications

Home Page: https://www.npmjs.com/package/eslint-plugin-angular

JavaScript 100.00%
eslint-plugin javascript angularjs

eslint-plugin-angular's Introduction

eslint plugin angular Npm version Npm downloads per month

Greenkeeper badge

ESLint rules for your angular project with checks for best-practices, conventions or potential errors.

Build Status Npm dependencies devDependency Status Join the chat at https://gitter.im/Gillespie59/eslint-plugin-angular Coverage Status

Summary

This repository will give access to new rules for the ESLint tool. You should use it only if you are developing an AngularJS application.

Since the 0.0.4 release, some rules defined in John Papa's Guideline have been implemented. In the description below, you will have a link to the corresponding part of the guideline, in order to have more information.

Contents

Usage with shareable config

  1. Install eslint as a dev-dependency:

    npm install --save-dev eslint
  2. Install eslint-plugin-angular as a dev-dependency:

    npm install --save-dev eslint-plugin-angular
  3. Use the shareable config by adding it to your .eslintrc:

    extends: plugin:angular/johnpapa

Usage without shareable config

  1. Install eslint as a dev-dependency:

    npm install --save-dev eslint
  2. Install eslint-plugin-angular as a dev-dependency:

    npm install --save-dev eslint-plugin-angular
  3. Enable the plugin by adding it to your .eslintrc:

    plugins:
      - angular
  4. You can also configure these rules in your .eslintrc. All rules defined in this plugin have to be prefixed by 'angular/'

    plugins:
      - angular
    rules:
      - angular/controller_name: 0

Rules

Rules in eslint-plugin-angular are divided into several categories to help you better understand their value.

Possible Errors

The following rules detect patterns that can lead to errors.

Best Practices

These are rules designed to prevent you from making mistakes. They either prescribe a better way of doing something or help you avoid footguns..

Deprecated Angular Features

These rules prevent you from using deprecated angular features.

Naming

These rules help you to specify several naming conventions.

Conventions

Angular often provide multi ways to to something. These rules help you to define convention for your project.

  • di-order - require DI parameters to be sorted alphabetically
  • di - require a consistent DI syntax
  • dumb-inject - unittest inject functions should only consist of assignments from injected values to describe block variables
  • function-type - require and specify a consistent function style for components ('named' or 'anonymous') (y024)
  • module-dependency-order - require a consistent order of module dependencies
  • no-service-method - use factory() instead of service() (y040)
  • one-dependency-per-line - require all DI parameters to be located in their own line
  • rest-service - disallow different rest service and specify one of '$http', '$resource', 'Restangular'
  • watchers-execution - require and specify consistent use $scope.digest() or $scope.apply()

Angular Wrappers

These rules help you to enforce the usage of angular wrappers.

Misspelling

These rules help you avoiding misspellings.

  • on-destroy - Check for common misspelling $on('destroy', ...).

Need your help

It is an opensource project. Any help will be very useful. You can :

  • Create issue
  • Send Pull Request
  • Write Documentation
  • Add new Features
  • Add new Rules
  • Improve the quality
  • Reply to issues

All development happens on the development branch. This means all pull requests should be made to the development branch.

If it is time to release, @Gillespie59 will bump the version in package.json, create a Git tag and merge the development branch into master. @Gillespie59 will then publish the new release to the npm registry.

How to create a new rule

We appreciate contributions and the following notes will help you before you open a Pull Request.

Check the issues

Have a look at the existing issues. There may exist similar issues with useful information.

Read the documentation

There are some useful references for creating new rules. Specificly useful are:

  • The Context Object - This is the most basic understanding needed for adding or modifying a rule.
  • Options Schemas - This is the preferred way for validating configuration options.
  • Scope - This is the scope object returned by context.getScope().

Files you have to create

  • rules/<your-rule>.js
    • JavaScript file with the new rule
    • The filename <your-rule> is exactly the usage name in eslint configs angular/<your-rule>
    • Have a look at the angularRule wrapper and the utils (both in rules/utils/) - they probably make things easier for you
    • Add a documentation comment to generate a markdown documentation with the gulp docs task
  • test/<your-rule>.js
    • Write some tests and execute them with gulp test
    • Have a look at the coverage reports coverage/lcov-report/index.html
  • examples/<your-rule>.js
    • Add some examples for the documentation
    • Run the gulp docs task to test the examples and update the markdown documentation
  • docs/<your-rule>.md
    • Generated by the gulp docs task

Files you have to touch

  • index.js
    • Add your rule rulesConfiguration.addRule('<your-rule>', [0, {someConfig: 'someValue'}])

Before you open your PR

  • Check that the gulp task is working
  • Commit generated changes in README.md and docs/<your-rule>.md
  • Open your PR to the development branch NOT master

Rules specific for Angular 1 or 2

We can use a property, defined in the ESLint configuration file, in order to know which version is used : Angular 1 or Angular 2. based on this property, you can create rules for each version.

plugins:
  - angular

rules:
    angular/controller-name:
      - 2
      - '/[A-Z].*Controller$/'

globals:
    angular: true

settings:
    angular: 2

And in your rule, you can access to this property thanks to the context object :

//If Angular 2 is used, we disabled the rule
if(context.settings.angular === 2){
    return {};
}

return {

    'CallExpression': function(node) {
    }
};

Default ESLint configuration file

Here is the basic configuration for the rules defined in the ESLint plugin, in order to be compatible with the guideline provided by @johnpapa :

rules:
    no-use-before-define:
      - 0

Who uses it?

Team

Emmanuel Demey Tilman Potthof Remco Haszing
Emmanuel Demey Tilman Potthof Remco Haszing

eslint-plugin-angular's People

Contributors

0cv avatar abhijeetkpawar avatar alecxe avatar amilajack avatar ank91 avatar bob-thomas avatar borislavjivkov avatar davidsonalencar avatar dphengsiaroun avatar dustinspecker avatar emmanueldemey avatar fchiumeo avatar grabus avatar greenkeeper[bot] avatar gustavderdrache avatar j-f1 avatar jarekb84 avatar jbalboni avatar jfgreffier avatar kayoub5 avatar lpcmedia avatar orisomething avatar palortoff avatar rayners avatar remcohaszing avatar robertmujica avatar sukrosono avatar tilmanschweitzer avatar tpeiffer avatar xdjinnx 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

eslint-plugin-angular's Issues

Assigning Controllers (Y038)

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • When a controller must be paired with a view and either component may be re-used by other controllers or views, define controllers along with their routes.

    Note: If a View is loaded via another means besides a route, then use the ng-controller="Avengers as vm" syntax.

    Why?: Pairing the controller in the route allows different routes to invoke different pairs of controllers and views. When controllers are assigned in the view using ng-controller, that view is always associated with the same controller.

    /* avoid - when using with a route and dynamic pairing is desired */
    
    // route-config.js
    angular
        .module('app')
        .config(config);
    
    function config($routeProvider) {
        $routeProvider
            .when('/avengers', {
              templateUrl: 'avengers.html'
            });
    }
    <!-- avengers.html -->
    <div ng-controller="Avengers as vm">
    </div>
    /* recommended */
    
    // route-config.js
    angular
        .module('app')
        .config(config);
    
    function config($routeProvider) {
        $routeProvider
            .when('/avengers', {
                templateUrl: 'avengers.html',
                controller: 'Avengers',
                controllerAs: 'vm'
            });
    }
    <!-- avengers.html -->
    <div>
    </div>

controllerAs Controller Syntax (Y031)

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • Use the controllerAs syntax over the classic controller with $scope syntax.

    • The controllerAs syntax uses this inside controllers which gets bound to $scope

    Why?: controllerAs is syntactic sugar over $scope. You can still bind to the View and still access $scope methods.

    Why?: Helps avoid the temptation of using $scope methods inside a controller when it may otherwise be better to avoid them or move them to a factory. Consider using $scope in a factory, or if in a controller just when needed. For example when publishing and subscribing events using $emit, $broadcast, or $on consider moving these uses to a factory and invoke from the controller.

    /* avoid */
    function Customer($scope) {
        $scope.name = {};
        $scope.sendMessage = function() { };
    }
    /* recommended - but see next section */
    function Customer() {
        this.name = {};
        this.sendMessage = function() { };
    }

Directives and ControllerAs (Y075)

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • Use controller as syntax with a directive to be consistent with using controller as with view and controller pairings.

    Why?: It makes sense and it's not difficult.

    Note: The directive below demonstrates some of the ways you can use scope inside of link and directive controllers, using controllerAs. I in-lined the template just to keep it all in one place.

    Note: Regarding dependency injection, see Manually Identify Dependencies.

    Note: Note that the directive's controller is outside the directive's closure. This style eliminates issues where the injection gets created as unreachable code after a return.

    <div my-example max="77"></div>
    angular
        .module('app')
        .directive('myExample', myExample);
    
    function myExample() {
        var directive = {
            restrict: 'EA',
            templateUrl: 'app/feature/example.directive.html',
            scope: {
                max: '='
            },
            link: linkFunc,
            controller: ExampleController,
              controllerAs: 'vm',
              bindToController: true // because the scope is isolated
          };
    
        return directive;
    
        function linkFunc(scope, el, attr, ctrl) {
            console.log('LINK: scope.min = %s *** should be undefined', scope.min);
            console.log('LINK: scope.max = %s *** should be undefined', scope.max);
            console.log('LINK: scope.vm.min = %s', scope.vm.min);
            console.log('LINK: scope.vm.max = %s', scope.vm.max);
        }
    }
    
    ExampleController.$inject = ['$scope'];
    
    function ExampleController($scope) {
        // Injecting $scope just for comparison
        var vm = this;
    
        vm.min = 3;
    
        console.log('CTRL: $scope.vm.min = %s', $scope.vm.min);
        console.log('CTRL: $scope.vm.max = %s', $scope.vm.max);
        console.log('CTRL: vm.min = %s', vm.min);
        console.log('CTRL: vm.max = %s', vm.max);
    }
    <!-- example.directive.html -->
    <div>hello world</div>
    <div>max={{vm.max}}<input ng-model="vm.max"/></div>
    <div>min={{vm.min}}<input ng-model="vm.min"/></div>

Function Declarations to Hide Implementation Details (Y053)

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • Use function declarations to hide implementation details. Keep your accessible members of the factory up top. Point those to function declarations that appears later in the file. For more details see this post.

    Why?: Placing accessible members at the top makes it easy to read and helps you instantly identify which functions of the factory you can access externally.

    Why?: Placing the implementation details of a function later in the file moves that complexity out of view so you can see the important stuff up top.

    Why?: Function declaration are hoisted so there are no concerns over using a function before it is defined (as there would be with function expressions).

    Why?: You never have to worry with function declarations that moving var a before var b will break your code because a depends on b.

    Why?: Order is critical with function expressions

    /**
    * avoid
    * Using function expressions
    */
    function dataservice($http, $location, $q, exception, logger) {
      var isPrimed = false;
      var primePromise;
    
      var getAvengers = function() {
          // implementation details go here
      };
    
      var getAvengerCount = function() {
          // implementation details go here
      };
    
      var getAvengersCast = function() {
         // implementation details go here
      };
    
      var prime = function() {
         // implementation details go here
      };
    
      var ready = function(nextPromises) {
          // implementation details go here
      };
    
      var service = {
          getAvengersCast: getAvengersCast,
          getAvengerCount: getAvengerCount,
          getAvengers: getAvengers,
          ready: ready
      };
    
      return service;
    }
    /**
    * recommended
    * Using function declarations
    * and accessible members up top.
    */
    function dataservice($http, $location, $q, exception, logger) {
        var isPrimed = false;
        var primePromise;
    
        var service = {
            getAvengersCast: getAvengersCast,
            getAvengerCount: getAvengerCount,
            getAvengers: getAvengers,
            ready: ready
        };
    
        return service;
    
        ////////////
    
        function getAvengers() {
            // implementation details go here
        }
    
        function getAvengerCount() {
            // implementation details go here
        }
    
        function getAvengersCast() {
            // implementation details go here
        }
    
        function prime() {
            // implementation details go here
        }
    
        function ready(nextPromises) {
            // implementation details go here
        }
    }

Keep Controllers Focused (Y037)

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • Define a controller for a view, and try not to reuse the controller for other views. Instead, move reusable logic to factories and keep the controller simple and focused on its view.

    Why?: Reusing controllers with several views is brittle and good end to end (e2e) test coverage is required to ensure stability across large applications.

Assigning Controllers

[Style Y038]
  • When a controller must be paired with a view and either component may be re-used by other controllers or views, define controllers along with their routes.

    Note: If a View is loaded via another means besides a route, then use the ng-controller="Avengers as vm" syntax.

    Why?: Pairing the controller in the route allows different routes to invoke different pairs of controllers and views. When controllers are assigned in the view using ng-controller, that view is always associated with the same controller.

    /* avoid - when using with a route and dynamic pairing is desired */
    
    // route-config.js
    angular
        .module('app')
        .config(config);
    
    function config($routeProvider) {
        $routeProvider
            .when('/avengers', {
              templateUrl: 'avengers.html'
            });
    }
    <!-- avengers.html -->
    <div ng-controller="Avengers as vm">
    </div>
    /* recommended */
    
    // route-config.js
    angular
        .module('app')
        .config(config);
    
    function config($routeProvider) {
        $routeProvider
            .when('/avengers', {
                templateUrl: 'avengers.html',
                controller: 'Avengers',
                controllerAs: 'vm'
            });
    }
    <!-- avengers.html -->
    <div>
    </div>

Errors when specifying rules in .eslintrc

When I specify rules in my .eslintc file, I'm seeing two results.

'ng_angularelement': 0,

This results in the rule still being run with the default option from rulesConfig in the plugin's index.js.

'ng_angularelement': 1

or

'ng_angularelement': 2

This results in the following error:

Warning: Definition for rule 'ng_definedundefined' was not found. Use --force to continue.

Route Resolve Promises (Y081)

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • When a controller depends on a promise to be resolved before the controller is activated, resolve those dependencies in the $routeProvider before the controller logic is executed. If you need to conditionally cancel a route before the controller is activated, use a route resolver.

    • Use a route resolve when you want to decide to cancel the route before ever transitioning to the View.

      Why?: A controller may require data before it loads. That data may come from a promise via a custom factory or $http. Using a route resolve allows the promise to resolve before the controller logic executes, so it might take action based on that data from the promise.

      Why?: The code executes after the route and in the controllerโ€™s activate function. The View starts to load right away. Data binding kicks in when the activate promise resolves. A โ€œbusyโ€ animation can be shown during the view transition (via ng-view or ui-view)

      Note: The code executes before the route via a promise. Rejecting the promise cancels the route. Resolve makes the new view wait for the route to resolve. A โ€œbusyโ€ animation can be shown before the resolve and through the view transition. If you want to get to the View faster and do not require a checkpoint to decide if you can get to the View, consider the controller activate technique instead.

    /* avoid */
    angular
        .module('app')
        .controller('Avengers', Avengers);
    
    function Avengers(movieService) {
        var vm = this;
        // unresolved
        vm.movies;
        // resolved asynchronously
        movieService.getMovies().then(function(response) {
            vm.movies = response.movies;
        });
    }
    /* better */
    
    // route-config.js
    angular
        .module('app')
        .config(config);
    
    function config($routeProvider) {
        $routeProvider
            .when('/avengers', {
                templateUrl: 'avengers.html',
                controller: 'Avengers',
                controllerAs: 'vm',
                resolve: {
                    moviesPrepService: function(movieService) {
                        return movieService.getMovies();
                    }
                }
            });
    }
    
    // avengers.js
    angular
        .module('app')
        .controller('Avengers', Avengers);
    
    Avengers.$inject = ['moviesPrepService'];
    function Avengers(moviesPrepService) {
          var vm = this;
          vm.movies = moviesPrepService.movies;
    }

    Note: The example below shows the route resolve points to a named function, which is easier to debug and easier to handle dependency injection.

    /* even better */
    
    // route-config.js
    angular
        .module('app')
        .config(config);
    
    function config($routeProvider) {
        $routeProvider
            .when('/avengers', {
                templateUrl: 'avengers.html',
                controller: 'Avengers',
                controllerAs: 'vm',
                resolve: {
                    moviesPrepService: moviesPrepService
                }
            });
    }
    
    function moviePrepService(movieService) {
        return movieService.getMovies();
    }
    
    // avengers.js
    angular
        .module('app')
        .controller('Avengers', Avengers);
    
    Avengers.$inject = ['moviesPrepService'];
    function Avengers(moviesPrepService) {
          var vm = this;
          vm.movies = moviesPrepService.movies;
    }

    Note: The code example's dependency on movieService is not minification safe on its own. For details on how to make this code minification safe, see the sections on dependency injection and on minification and annotation.

Accessible Members Up Top (Y052)

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • Expose the callable members of the service (its interface) at the top, using a technique derived from the Revealing Module Pattern.

    Why?: Placing the callable members at the top makes it easy to read and helps you instantly identify which members of the service can be called and must be unit tested (and/or mocked).

    Why?: This is especially helpful when the file gets longer as it helps avoid the need to scroll to see what is exposed.

    Why?: Setting functions as you go can be easy, but when those functions are more than 1 line of code they can reduce the readability and cause more scrolling. Defining the callable interface via the returned service moves the implementation details down, keeps the callable interface up top, and makes it easier to read.

    /* avoid */
    function dataService() {
      var someValue = '';
      function save() {
        /* */
      };
      function validate() {
        /* */
      };
    
      return {
          save: save,
          someValue: someValue,
          validate: validate
      };
    }
    /* recommended */
    function dataService() {
        var someValue = '';
        var service = {
            save: save,
            someValue: someValue,
            validate: validate
        };
        return service;
    
        ////////////
    
        function save() {
            /* */
        };
    
        function validate() {
            /* */
        };
    }

    This way bindings are mirrored across the host object, primitive values cannot update alone using the revealing module pattern

Manually Identify Dependencies (Y091)

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • Use $inject to manually identify your dependencies for AngularJS components.

    Why?: This technique mirrors the technique used by ng-annotate, which I recommend for automating the creation of minification safe dependencies. If ng-annotate detects injection has already been made, it will not duplicate it.

    Why?: This safeguards your dependencies from being vulnerable to minification issues when parameters may be mangled. For example, common and dataservice may become a or b and not be found by AngularJS.

    Why?: Avoid creating in-line dependencies as long lists can be difficult to read in the array. Also it can be confusing that the array is a series of strings while the last item is the component's function.

    /* avoid */
    angular
        .module('app')
        .controller('Dashboard',
            ['$location', '$routeParams', 'common', 'dataservice',
                function Dashboard($location, $routeParams, common, dataservice) {}
            ]);
    /* avoid */
    angular
      .module('app')
      .controller('Dashboard',
          ['$location', '$routeParams', 'common', 'dataservice', Dashboard]);
    
    function Dashboard($location, $routeParams, common, dataservice) {
    }
    /* recommended */
    angular
        .module('app')
        .controller('Dashboard', Dashboard);
    
    Dashboard.$inject = ['$location', '$routeParams', 'common', 'dataservice'];
    
    function Dashboard($location, $routeParams, common, dataservice) {
    }

    Note: When your function is below a return statement the $inject may be unreachable (this may happen in a directive). You can solve this by either moving the $inject above the return statement or by using the alternate array injection syntax.

    Note: ng-annotate 0.10.0 introduced a feature where it moves the $inject to where it is reachable.

    // inside a directive definition
    function outer() {
        return {
            controller: DashboardPanel,
        };
    
        DashboardPanel.$inject = ['logger']; // Unreachable
        function DashboardPanel(logger) {
        }
    }
    // inside a directive definition
    function outer() {
        DashboardPanel.$inject = ['logger']; // reachable
        return {
            controller: DashboardPanel,
        };
    
        function DashboardPanel(logger) {
        }
    }

Directives - Limit 1 Per File (Y070)

https://github.com/johnpapa/angularjs-styleguide/blob/master/README.md

  • Create one directive per file. Name the file for the directive.

    Why?: It is easy to mash all the directives in one file, but difficult to then break those out so some are shared across apps, some across modules, some just for one module.

    Why?: One directive per file is easy to maintain.

    /* avoid */
    /* directives.js */
    
    angular
        .module('app.widgets')
    
        /* order directive that is specific to the order module */
        .directive('orderCalendarRange', orderCalendarRange)
    
        /* sales directive that can be used anywhere across the sales app */
        .directive('salesCustomerInfo', salesCustomerInfo)
    
        /* spinner directive that can be used anywhere across apps */
        .directive('sharedSpinner', sharedSpinner);
    
    function orderCalendarRange() {
        /* implementation details */
    }
    
    function salesCustomerInfo() {
        /* implementation details */
    }
    
    function sharedSpinner() {
        /* implementation details */
    }
    /* recommended */
    /* calendarRange.directive.js */
    
    /**
    * @desc order directive that is specific to the order module at a company named Acme
    * @example <div acme-order-calendar-range></div>
    */
    angular
        .module('sales.order')
        .directive('acmeOrderCalendarRange', orderCalendarRange);
    
    function orderCalendarRange() {
        /* implementation details */
    }
    /* recommended */
    /* customerInfo.directive.js */
    
    /**
    * @desc spinner directive that can be used anywhere across the sales app at a company named Acme
    * @example <div acme-sales-customer-info></div>
    */
    angular
        .module('sales.widgets')
        .directive('acmeSalesCustomerInfo', salesCustomerInfo);
    
    function salesCustomerInfo() {
        /* implementation details */
    }
    /* recommended */
    /* spinner.directive.js */
    
    /**
    * @desc spinner directive that can be used anywhere across apps at a company named Acme
    * @example <div acme-shared-spinner></div>
    */
    angular
        .module('shared.widgets')
        .directive('acmeSharedSpinner', sharedSpinner);
    
    function sharedSpinner() {
        /* implementation details */
    }

    Note: There are many naming options for directives, especially since they can be used in narrow or wide scopes. Choose one that makes the directive and its file name distinct and clear. Some examples are below, but see the naming section for more recommendations.

Controller Activation Promises (Y080)

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • Resolve start-up logic for a controller in an activate function.

    Why?: Placing start-up logic in a consistent place in the controller makes it easier to locate, more consistent to test, and helps avoid spreading out the activation logic across the controller.

    Why?: The controller activate makes it convenient to re-use the logic for a refresh for the controller/View, keeps the logic together, gets the user to the View faster, makes animations easy on the ng-view or ui-view, and feels snappier to the user.

    Note: If you need to conditionally cancel the route before you start use the controller, use a route resolve instead.

    /* avoid */
    function Avengers(dataservice) {
        var vm = this;
        vm.avengers = [];
        vm.title = 'Avengers';
    
        dataservice.getAvengers().then(function(data) {
            vm.avengers = data;
            return vm.avengers;
        });
    }
    /* recommended */
    function Avengers(dataservice) {
        var vm = this;
        vm.avengers = [];
        vm.title = 'Avengers';
    
        activate();
    
        ////////////
    
        function activate() {
            return dataservice.getAvengers().then(function(data) {
                vm.avengers = data;
                return vm.avengers;
            });
        }
    }

controllerAs with vm (Y032)

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • Use a capture variable for this when using the controllerAs syntax. Choose a consistent variable name such as vm, which stands for ViewModel.

    Why?: The this keyword is contextual and when used within a function inside a controller may change its context. Capturing the context of this avoids encountering this problem.

    /* avoid */
    function Customer() {
        this.name = {};
        this.sendMessage = function() { };
    }
    /* recommended */
    function Customer() {
        var vm = this;
        vm.name = {};
        vm.sendMessage = function() { };
    }

    Note: You can avoid any jshint warnings by placing the comment below above the line of code. However it is not needed when the function is named using UpperCasing, as this convention means it is a constructor function, which is what a controller is in Angular.

    /* jshint validthis: true */
    var vm = this;

    Note: When creating watches in a controller using controller as, you can watch the vm.* member using the following syntax. (Create watches with caution as they add more load to the digest cycle.)

    <input ng-model="vm.title"/>
    function SomeController($scope, $log) {
        var vm = this;
        vm.title = 'Some Title';
    
        $scope.$watch('vm.title', function(current, original) {
            $log.info('vm.title was %s', original);
            $log.info('vm.title is now %s', current);
        });
    }

Singletons (Y040)

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • Services are instantiated with the new keyword, use this for public methods and variables. Since these are so similar to factories, use a factory instead for consistency.

    Note: All AngularJS services are singletons. This means that there is only one instance of a given service per injector.

    // service
    angular
        .module('app')
        .service('logger', logger);
    
    function logger() {
      this.logError = function(msg) {
        /* */
      };
    }
    // factory
    angular
        .module('app')
        .factory('logger', logger);
    
    function logger() {
        return {
            logError: function(msg) {
              /* */
            }
       };
    }

Anonymous or Named funtions inside AngularJS components

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • Use named functions instead of passing an anonymous function in as a callback.

    Why?: This produces more readable code, is much easier to debug, and reduces the amount of nested callback code.

    /* avoid */
    angular
        .module('app')
        .controller('Dashboard', function() { })
        .factory('logger', function() { });
    /* recommended */
    
    // dashboard.js
    angular
        .module('app')
        .controller('Dashboard', Dashboard);
    
    function Dashboard() { }
    // logger.js
    angular
        .module('app')
        .factory('logger', logger);
    
    function logger() { }

Restrict to Elements and Attributes (Y074)

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • When creating a directive that makes sense as a stand-alone element, allow restrict E (custom element) and optionally restrict A (custom attribute). Generally, if it could be its own control, E is appropriate. General guideline is allow EA but lean towards implementing as an element when it's stand-alone and as an attribute when it enhances its existing DOM element.

    Why?: It makes sense.

    Why?: While we can allow the directive to be used as a class, if the directive is truly acting as an element it makes more sense as an element or at least as an attribute.

    Note: EA is the default for AngularJS 1.3 +

    <!-- avoid -->
    <div class="my-calendar-range"></div>
    /* avoid */
    angular
        .module('app.widgets')
        .directive('myCalendarRange', myCalendarRange);
    
    function myCalendarRange() {
        var directive = {
            link: link,
            templateUrl: '/template/is/located/here.html',
            restrict: 'C'
        };
        return directive;
    
        function link(scope, element, attrs) {
          /* */
        }
    }
    <!-- recommended -->
    <my-calendar-range></my-calendar-range>
    <div my-calendar-range></div>
    /* recommended */
    angular
        .module('app.widgets')
        .directive('myCalendarRange', myCalendarRange);
    
    function myCalendarRange() {
        var directive = {
            link: link,
            templateUrl: '/template/is/located/here.html',
            restrict: 'EA'
        };
        return directive;
    
        function link(scope, element, attrs) {
          /* */
        }
    }

Check the Karma configuration file

Based on the name of the file (configurable), we should be able to lint the Karma Configuration file, in order to be sure to use the right properties

Route Errors (Y112)

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • Handle and log all routing errors using $routeChangeError.

    Why?: Provides a consistent way to handle all routing errors.

    Why?: Potentially provides a better user experience if a routing error occurs and you route them to a friendly screen with more details or recovery options.

    /* recommended */
    var handlingRouteChangeError = false;
    
    function handleRoutingErrors() {
        /**
         * Route cancellation:
         * On routing error, go to the dashboard.
         * Provide an exit clause if it tries to do it twice.
         */
        $rootScope.$on('$routeChangeError',
            function(event, current, previous, rejection) {
                if (handlingRouteChangeError) { return; }
                handlingRouteChangeError = true;
                var destination = (current && (current.title ||
                    current.name || current.loadedTemplateUrl)) ||
                    'unknown target';
                var msg = 'Error routing to ' + destination + '. ' +
                    (rejection.msg || '');
    
                /**
                 * Optionally log using a custom service or $log.
                 * (Don't forget to inject custom service)
                 */
                logger.warning(msg, [current]);
    
                /**
                 * On routing error, go to another route/state.
                 */
                $location.path('/');
    
            }
        );
    }

Getters (Y022)

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • When using a module, avoid using a variable and instead use chaining with the getter syntax.

    Why?: This produces more readable code and avoids variable collisions or leaks.

    /* avoid */
    var app = angular.module('app');
    app.controller('SomeController', SomeController);
    
    function SomeController() { }
    /* recommended */
    angular
        .module('app')
        .controller('SomeController', SomeController);
    
    function SomeController() { }

Function Declarations to Hide Implementation Details (Y034)

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • Use function declarations to hide implementation details. Keep your bindable members up top. When you need to bind a function in a controller, point it to a function declaration that appears later in the file. This is tied directly to the section Bindable Members Up Top. For more details see this post.

    Why?: Placing bindable members at the top makes it easy to read and helps you instantly identify which members of the controller can be bound and used in the View. (Same as above.)

    Why?: Placing the implementation details of a function later in the file moves that complexity out of view so you can see the important stuff up top.

    Why?: Function declaration are hoisted so there are no concerns over using a function before it is defined (as there would be with function expressions).

    Why?: You never have to worry with function declarations that moving var a before var b will break your code because a depends on b.

    Why?: Order is critical with function expressions

    /**
    * avoid
    * Using function expressions.
    */
    function Avengers(dataservice, logger) {
        var vm = this;
        vm.avengers = [];
        vm.title = 'Avengers';
    
        var activate = function() {
            return getAvengers().then(function() {
                logger.info('Activated Avengers View');
            });
        }
    
        var getAvengers = function() {
            return dataservice.getAvengers().then(function(data) {
                vm.avengers = data;
                return vm.avengers;
            });
        }
    
        vm.getAvengers = getAvengers;
    
        activate();
    }

    Notice that the important stuff is scattered in the preceding example. In the example below, notice that the important stuff is up top. For example, the members bound to the controller such as vm.avengers and vm.title. The implementation details are down below. This is just easier to read.

    /*
    * recommend
    * Using function declarations
    * and bindable members up top.
    */
    function Avengers(dataservice, logger) {
        var vm = this;
        vm.avengers = [];
        vm.getAvengers = getAvengers;
        vm.title = 'Avengers';
    
        activate();
    
        function activate() {
            return getAvengers().then(function() {
                logger.info('Activated Avengers View');
            });
        }
    
        function getAvengers() {
            return dataservice.getAvengers().then(function(data) {
                vm.avengers = data;
                return vm.avengers;
            });
        }
    }

Manipulate DOM in a Directive (Y072)

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • When manipulating the DOM directly, use a directive. If alternative ways can be used such as using CSS to set styles or the animation services, Angular templating, ngShow or ngHide, then use those instead. For example, if the directive simply hides and shows, use ngHide/ngShow.

    Why?: DOM manipulation can be difficult to test, debug, and there are often better ways (e.g. CSS, animations, templates)

Definitions (aka Setters) (Y021)

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • Declare modules without a variable using the setter syntax.

    Why?: With 1 component per file, there is rarely a need to introduce a variable for the module.

    /* avoid */
    var app = angular.module('app', [
        'ngAnimate',
        'ngRoute',
        'app.shared',
        'app.dashboard'
    ]);

    Instead use the simple setter syntax.

    /* recommended */
    angular
        .module('app', [
            'ngAnimate',
            'ngRoute',
            'app.shared',
            'app.dashboard'
        ]);

Configuration for client and server code

Not an issue, only an hint, if you wonder how to lint different part of code, this is my way. :)

- myproject
--- .eslintrc
--- src
------ client
--------- .eslintrc 
------ server
--------- .eslintrc
  • myproject/.eslintrc contains the common rules.
    For instance,
rules:
    brace-style: [2, "1tbs"]
    comma-style: [2, "last"]
    default-case: 2
    func-style: [2, "declaration"]
...   
  • src/client/.eslintrc contains client side rules.
    For instance,
env:
    browser: true

globals:
    angular: false

plugins:
  - angular
  • src/server/.eslintrc contains server side rules.
    For instance
env:
    node: true

Usually I lint a javascript file when I save it. ESLint is installed also globally. As stated in config help page of ESLint, a globally-installed instance of ESLint can only use globally-installed ESLint plugins. A locally-installed ESLint can make sure of both locally- and globally- installed ESLint plugins.
That's I need to install also globally the plugin.

Define 1 component per page (Y001)

https://github.com/johnpapa/angularjs-styleguide

  • Define 1 component per file.

    The following example defines the app module and its dependencies, defines a controller, and defines a factory all in the same file.

    /* avoid */
    angular
        .module('app', ['ngRoute'])
        .controller('SomeController', SomeController)
        .factory('someFactory', someFactory);
    
    function SomeController() { }
    
    function someFactory() { }

    The same components are now separated into their own files.

    /* recommended */
    
    // app.module.js
    angular
        .module('app', ['ngRoute']);
    /* recommended */
    
    // someController.js
    angular
        .module('app')
        .controller('SomeController', SomeController);
    
    function SomeController() { }
    /* recommended */
    
    // someFactory.js
    angular
        .module('app')
        .factory('someFactory', someFactory);
    
    function someFactory() { }

Bindable Members Up Top (Y033)

https://github.com/johnpapa/angularjs-styleguide/edit/master/README.md

  • Place bindable members at the top of the controller, alphabetized, and not spread through the controller code.

    Why?: Placing bindable members at the top makes it easy to read and helps you instantly identify which members of the controller can be bound and used in the View.

    Why?: Setting anonymous functions in-line can be easy, but when those functions are more than 1 line of code they can reduce the readability. Defining the functions below the bindable members (the functions will be hoisted) moves the implementation details down, keeps the bindable members up top, and makes it easier to read.

    /* avoid */
    function Sessions() {
        var vm = this;
    
        vm.gotoSession = function() {
          /* ... */
        };
        vm.refresh = function() {
          /* ... */
        };
        vm.search = function() {
          /* ... */
        };
        vm.sessions = [];
        vm.title = 'Sessions';
    /* recommended */
    function Sessions() {
        var vm = this;
    
        vm.gotoSession = gotoSession;
        vm.refresh = refresh;
        vm.search = search;
        vm.sessions = [];
        vm.title = 'Sessions';
    
        ////////////
    
        function gotoSession() {
          /* */
        }
    
        function refresh() {
          /* */
        }
    
        function search() {
          /* */
        }

    Controller Using "Above the Fold"

    Note: If the function is a 1 liner consider keeping it right up top, as long as readability is not affected.

    /* avoid */
    function Sessions(data) {
        var vm = this;
    
        vm.gotoSession = gotoSession;
        vm.refresh = function() {
            /**
             * lines
             * of
             * code
             * affects
             * readability
             */
        };
        vm.search = search;
        vm.sessions = [];
        vm.title = 'Sessions';
    /* recommended */
    function Sessions(dataservice) {
        var vm = this;
    
        vm.gotoSession = gotoSession;
        vm.refresh = dataservice.refresh; // 1 liner is OK
        vm.search = search;
        vm.sessions = [];
        vm.title = 'Sessions';

Check the Protractor configuration file

Based on the name of the file (configurable), we should be able to lint the Protractor Configuration file, in order to be sure to use the right properties

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.