Force delay controller logic

We are trying to apply the recommendations listed in the John Papa Handbook AngularJS Style Guide .

One of the rules we started to follow is to set aside the controller logic :

Set aside logic in the controller, delegating to services and factories.

Why ?: Logic can be reused by several controllers when placed inside the service and display through the function.

Why ?: The logic in the service is easier to isolate in the unit test, while the logic of the call in the controller can be easily ridiculed.

Why ?: Removes dependencies and hides implementation details from the controller.

This is something that we violated in the past by putting data retrieval logic in controllers, rather than isolating it in a service.

Now I would like to make the rule as strict as possible. Ideally, I would like angular to throw an error if one of the configured services is passed as a controller dependency. Is this something that can be solved at the angular level, or should I try to solve it separately - for example, statically using a custom rule ESlint?

We hit any ideas or tips.


In particular, the following controller breaks the rule because it directly uses $httpservice :

function OrderController($http, $q, config, userInfo) {
    var vm = this;
    vm.checkCredit = checkCredit;
    vm.isCreditOk;

    function checkCredit() {
        var settings = {};

        return $http.get(settings)
            .then(function(data) {
               vm.isCreditOk = vm.total <= maxRemainingAmount;
            })
            .catch(function(error) {

            });
    };
}

Also, let me know if I'm too concerned about the quality of the code :)

+4
source share
2 answers

UPD ( 2015): eslint-plugin-angular ng_no_services, .


.

ESLint , , Controller Ctrl, , :

module.exports = function (context) {

    "use strict";

    var restrictedParams = context.options[0] || [];

    var check = function (node) {
        var name = node.id && node.id.name;

        if (/(Controller|Ctrl)$/.test(name) && node.params) {
            var params = node.params.map(
                function (param) {
                    return param.name;
                }
            );

            restrictedParams.filter(function (n) {
                if (params.indexOf(n) != -1) {
                    context.report(node, "This controller function uses a restricted dependency {{param}}.", {
                        param: n
                    });
                }
            });

        }
    };

    return {
        "FunctionDeclaration": check,
        "FunctionExpression": check
    }
};

(eslint.json):

{
  "env": {
    "browser": true,
    "node": true,
    "jasmine": true
  },
  "globals": {
    "angular": true,
    "browser": true,
    "element": true,
    "by": false,
    "inject": false
  },
  "plugins": [
    "angularjs"
  ],
  "rules": {
    "ctrl-logic": [2, ["$http"]]
  }
}

, , ( ):

angular
    .module("app")
    .controller("AppController", AppController);


AppController.$inject = ["$scope", "ConnectionService", "ConfigService", "StatusService", "$http"];

function AppController($scope, ConnectionService, ConfigService, StatusService, $http) {
    ...
}

ESLint, :

$ grunt lint
Running "eslint:target" (eslint) task

app/app-controller.js
  8:0  error  This controller function uses a restricted dependency $http  ctrl-logic

✖ 1 problem

, , , , , , ( ).

+4

, , :

<!doctype html>
<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.3/angular.js"></script>
    </head>
    <body ng-app="plunker" ng-controller="MainCtrl">
        <div ng-repeat="violation in codestyleViolations">
            {{violation}}
        </div>
    </body>
    <script>
    var app = angular.module('plunker', []).config(function($controllerProvider) { 
        $controllerProvider.$get[0] = 'trickyInjector';
    }).factory('trickyInjector', ['$injector', '$rootScope', function($injector, $rootScope) {
        $rootScope.codestyleViolations = [];
        var originalFunc = $injector.invoke;
        $injector.invoke = function(fn, self, locals, serviceName) {
            if (locals && locals.$scope && fn.indexOf('$http') !== -1) {
                $rootScope.codestyleViolations.push('ALYARM! ' + serviceName + ' uses $http');
            }
            return originalFunc.apply(this, arguments);
        };
        return $injector;
    }]).controller('MainCtrl', ['$scope', '$http', function($scope, $http) {

    }]);
    </script>
</html>
Hide result
+2

Source: https://habr.com/ru/post/1570210/


All Articles