使用指令以角度分割控制器逻辑是一种好习惯吗?

时间:2016-02-19 17:31:34

标签: angularjs angularjs-directive

我有非常复杂的控制器(大约3K行代码)来演示仪表板。 Controller包含许多图表,网格表等。

例如,我将网格表逻辑移动到下面提到的名为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>

所以在控制器HTML中,我只写:<wm-grid-action-items></wm-grid-action-items>

我无法在其他地方使用此指令,但至少我将我的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'
                        }
                    }
                })

谢谢,

2 个答案:

答案 0 :(得分:3)

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

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

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

  3. 根据以上两条建议,您的指令应如下所示:

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

答案 1 :(得分:1)

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

您构建的每个组件(指令/控制器/服务)绝不能做多件事。如果您避免此错误,您的组件将更具可重用性,可读性和可维护性。

当然,这种做法不仅仅是角度

你尊重的,我建议你:

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

  • 指令应仅包含DOM操作。

角度指令/控制器/服务是一种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
])