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 Issues

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>

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

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
        }
    }

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.

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() { };
    }

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';

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;
            });
        }
    }

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

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>

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.

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)

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

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);
        });
    }

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) {
        }
    }

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.

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() { }

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) {
          /* */
        }
    }

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;
            });
        }
    }

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() { }

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) {
              /* */
            }
       };
    }

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('/');
    
            }
        );
    }

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() { }

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>

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.

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.