【问题标题】:Is it good practice to use directives for splitting controller logic in angular?使用指令以角度拆分控制器逻辑是一种好习惯吗?
【发布时间】:2016-02-19 17:31:34
【问题描述】:

我有相当复杂的控制器(大约 3K 行代码)来演示仪表板。控制器包含很多图表、网格表等。

例如,我将网格表逻辑移动到下面提到的名为 wmGridActionItems 的指令中。注意,它使用父作用域:

app.directive('wmGridActionItems', ['$rootScope', '$timeout', function ($rootScope, $timeout) {
        return {
            restrict: 'E',
            templateUrl: 'views/grid-action-items.html',

            link: function (scope, elem, attrs) {

                // logic goes here
         }
        };
    }]);

和 HTML:

<div  ui-grid="gridActionItemsOptions"                           
                      ui-grid-auto-resize
                      ui-grid-pagination
                      ui-grid-selection
                      ui-grid-auto-resize
                      ui-grid-resize-columns>
                </div>

所以在 main 控制器 HTML 中我只写:&lt;wm-grid-action-items&gt;&lt;/wm-grid-action-items&gt;

我没有设法在其他地方使用这个指令,但至少我将我的 BIG 控制器划分为几个小指令,这些指令应该可以帮助我处理仪表板。

我做错了什么?这是好习惯吗? Angular 有其他方法可以解决这个问题吗?

编辑

这是我的$StateProvider 仪表板视图:

$stateProvider  
 .state('sidemenu.dash', {
                    url: '/dshmngr',
                    abstract: true,
                    views: {
                        'content': {
                            templateUrl: 'views/dashboard/dashboard_manager.html',
                            controller: 'DashboardMngrCtrl'
                        }
                    }


                })

                .state('sidemenu.dash.main', {
                    url: '/main',
                    views: {
                        'content': {
                            templateUrl: 'views/dashboard/dashboard-main.html',
                            controller: 'DashboardNewCtrl'
                        }
                    }
                })


                .state('sidemenu.dash.drill', {
                    url: '/drill/:type',
                    views: {
                        'content': {
                            templateUrl: 'views/dashboard/dashboard-tag-details.html',
                            controller: 'DashboardDetailedCtrl'
                        }
                    }
                })

谢谢,

【问题讨论】:

  • 与其将主控制器的逻辑放在指令中,不如制作另一个控制器并将逻辑放在那儿?没有什么能阻止您拥有多个控制器。
  • 好吧,我尝试解决当前的问题。通常我有 3 个带有抽象路由和几个子视图的控制器。每个视图都有自己的控制器。但是将一个视图拆分为多个控制器会导致逻辑过于复杂,广播、观察者等等
  • 作为一个好习惯,你不想在控制器中做太多的逻辑。这不仅适用于角度,而且适用于孔 MVC 框架。所以你会想要打破工厂或服务中指令的逻辑。正如@jperov 所说,您可以拥有多个控制器,甚至可以从一个控制器继承到另一个控制器
  • @snaggs 只是看到您的评论,您可以考虑继承子视图中的控制器。你可以在这里阅读更多:stackoverflow.com/questions/15386137/…

标签: angularjs angularjs-directive


【解决方案1】:

你的目标是正确的。将大型控制器以指令的形式分解成更小的组件是可行的方法,但我建议您进行一些更改。

  1. 隔离指令的范围并明确定义指令期望的数据。这样一来,人们就可以立即看到数据指令接受的内容。

  2. 为了便于测试,请将指令与 Controller 配对。

基于以上两个建议,您的指令应该如下所示:

app.directive('wmGridActionItems', [function () {
        return {
            controller: 'WmGridActionItemsController'
            restrict: 'E',
            templateUrl: 'views/grid-action-items.html',
            scope: {
               gridActionItemsOptions: '=gridActionItemsOptions' 
            }
            link: function (scope, elem, attrs) {
                // DOM manpulation (if needed) goes here
            }
        };
    }]);

app.controller('WmGridActionItemsController', ['$cope', '$timeout', function ($cope, $timeout) {
    // logic goes here
}]);

然后你会像这样调用上面的指令:

<wm-grid-action-items grid-action-item-options="gridActionItemsOptions">
</wm-grid-action-items>

我建议你也阅读这篇很棒的blog post,它详细解释了“组件模式”。

另请注意,通过在定义隔离范围时明确指定模型来共享模型并不是唯一的方法。共享数据的另一种替代方法是模型即服务(请参阅related reading)。

【讨论】:

  • 我喜欢 Component Pattern 你提供的帖子,谢谢
【解决方案2】:

我向您推荐的“良好做法”是single responsibility principle

您构建的每个组件(指令/控制器/服务)不得超过一件事。如果你避免了这个错误,你的组件将更加可重用、可读和可维护。

当然,这种做法不仅适用于 Angular

如果你尊重这一点,我会推荐你​​:

  • 避免将所有业务代码放在控制器中,而应使用服务(或提供程序)。服务更强大,因为它们允许使用角度依赖注入系统。

  • 指令应仅包含 DOM 操作。

Angular Directive/Controller/Service 是一种View/ViewModel/Model 模式。尽量记住这一点。

编辑:您的每个指令都可以有一个控制器。您可以将指令放在另一个指令模板中,然后使用链接函数的最后一个参数 (controllers) 和指令require 参数进行指令之间的通信。

示例:(coffeescript)假设我有一个容器,它可以在其他容器本身内,并且可能还包含一个小部件:

angular.module('dashboard')
.directive('dashboardWidget', [() ->
    restrict: 'E'
    templateUrl : '/views/dashboard/widget.html'
    require: ['^^dashboardContainer']
    scope:
        'model': '='
    controller: 'DashboardWidgetController'
    controllerAs: 'DashboardWidget'
    # default => post link (children already instanciated)
    link: ($scope, element, attrs, ctrls) ->
        [parentDashboardContainerController] = ctrls

        # some very small code (mainly events), the real code is in the controller

        return
])

angular.module('dashboard')
.directive('dashboardContainer', [() ->
    restrict: 'E'
    templateUrl : '/views/dashboard/container.html'
    require: ['?^^dashboardContainer', '?^ngController']
    scope:
        'model': '='
    controller: 'DashboardContainerController'
    controllerAs: 'DashboardContainer'
    # default => post link (children already instanciated)
    link: ($scope, element, attrs, ctrls) ->
        [parentDashboardContainerController, ngController] = ctrls

        # some very small code (mainly events), the real code is in the controller

        return
])

【讨论】:

  • 当我说about 3K rows of code 表示控制器逻辑NETO。 a.e.用户界面。实际上,所有业务逻辑都位于服务和工厂之下
  • 我很了解 MVC 模型,我知道指令是 UI 责任和 DOM 操作 ....
  • 你的每个指令都可以有一个控制器(带有'controller'属性)。因此,使用单一责任原则拆分您的代码,这应该更清晰。就个人而言,如果我的一个文件包含超过 200 行,我开始怀疑自己是否没有做太多事情。这是非常重要的事情,尤其是在大型项目中。你总有一天会回到你的文件,让它们易于阅读。
  • 同意,就像一个功能尺寸应该进入一个屏幕:)
  • 我添加了一些简单的例子。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-05
  • 2010-11-16
  • 2016-06-09
  • 2018-02-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多