【问题标题】:Enforce deferring controller logic强制执行延迟控制器逻辑
【发布时间】:2015-03-04 14:24:48
【问题描述】:

我们正在尝试应用 John Papa 的AngularJS Style Guide 中列出的指南。

我们开始遵循的规则之一是Defer Controller Logic:

通过委托给服务和工厂来延迟控制器中的逻辑。

为什么?:逻辑可以被多个控制器重用。 服务并通过函数公开。

为什么?:服务中的逻辑可以更容易地在单元测试中隔离, 而控制器中的调用逻辑很容易被mock。

Why?:移除依赖项并隐藏实现细节 控制器。

这是我们过去通过将数据检索逻辑放入控制器而不是将其隔离在服务中而违反的。

现在我想让规则尽可能严格。理想情况下,如果配置的服务之一作为依赖项传递给控制器​​,我希望 Angular 抛出错误。是否可以在角度级别上解决,或者我应该尝试单独解决它 - 例如,使用自定义 ESlint 规则静态解决?

希望有任何见解或提示。


特别是下面的控制器违反了规则,因为它直接使用了$http service:

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

            });
    };
}

另外,如果我对代码质量过于担心/疯狂,请告诉我:)

【问题讨论】:

  • 我会尝试使用 ESLint 并使用一个规则来阻止在控制器中使用 $http/$resource。我个人认为您应该在您的开发团队中进行讨论,并且在处理数据时让每个人都在同一页面上

标签: javascript angularjs angularjs-service angularjs-controller eslint


【解决方案1】:

UPD(2015 年 6 月):eslint-plugin-angular 具有内置的 ng_no_services 规则,该规则强制将控制器逻辑延迟到服务或工厂。


旧答案。

这里是 ESLint custom rule,它限制函数(名称以 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

我可以想象有一些方法可以定义依赖项,使该特定规则不会发现违规行为,但是,如果您遵循样式指南中建议的建议,它应该对您有用(对我有用)。

【讨论】:

    【解决方案2】:

    我写了一种解决方法,但它非常棘手和肮脏,绝对不能使用:

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

    【讨论】:

    • 我喜欢你的“永远不能使用”:D 谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多