Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it good practice to use directives for splitting controller logic in angular?

I have pretty complicated controller (about 3K rows of code) that demonstrates dashboard. Controller contains a lot of charts, grid tables, and so on.

For example I moved grid table logic to undermentioned directive named wmGridActionItems. Notice, it uses parent scope:

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

and HTML:

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

So in main controller HTML i just write: <wm-grid-action-items></wm-grid-action-items>

I don't manage to use this directive in other places but at least I divide my BIG controller to several little directives that should help me to handle dashboard.

I do something wrong? Is it good practice? Has Angular other approaches to solve this issue?

EDIT

This is my $StateProvider for dashboard view:

$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'
                        }
                    }
                })

Thanks,

like image 334
snaggs Avatar asked Feb 19 '16 17:02

snaggs


People also ask

Which directive is used for controller in Angular?

AngularJS ng-controller Directive The ng-controller directive adds a controller to your application. In the controller you can write code, and make functions and variables, which will be parts of an object, available inside the current HTML element. In AngularJS this object is called a scope.

Why do we use directives in Angular?

Directives are classes that add additional behavior to elements in your Angular applications. Use Angular's built-in directives to manage forms, lists, styles, and what users see.

What is the difference between AngularJS directives and controllers?

A controller is usually used to contain and maintain the logic for your view, which gets bound to your view via $scope. A directive is something that you might use repeatedly and is called in your view directly through the directive name which you can pass in as an attribute.

What is the difference between controller and link in directives?

Answer:The link option is just a shortcut to setting up a post-link function. controller: The directive controller can be passed to another directive linking/compiling phase. It can be injected into other directices as a mean to use in inter-directive communication.

What are the different types of directives in angular?

In Angular, there are three types of directives those are component directive, attribute directive and structural directive. A component directive is a simple class which is decorated with the @component decorator. In Angular, once typescript class has been decorated with a @component decorator, it will become a Component class.

What is code splitting in angular and why should I Care?

If you are scared from Angular CLI output showed above or if you’re curious how that code-splitting actually happens then this post is for you. Code splitting allows you to split your code into various bundles which can then be loaded on demand. If used correctly, can have a major impact on load time. Why should I care? Why should I care?

What is the difference between @component decorator and attribute directives in angular?

In Angular, once typescript class has been decorated with a @component decorator, it will become a Component class. It is mainly used to specify the HTML templates. Attribute directives are a way of changing the appearance or behavior of a component or a native DOM element. It is used to change the attributes of the existing HTML element.

What is the difference between a controller and a directive?

Directives are very similar to controllers in many ways. Here is a simple example of a directive that follows the same logic as the Angular controller that we discussed before. We have a controller here for the directive that handles the business logic, thus eliminating the need to add garbage to the $scope variable.


2 Answers

You are aiming at the right direction. Breaking a large controller into more smaller components in the form of directives is the way to go but I would suggest you to introduce few changes.

  1. Isolate the scope of directive and define the data that directive expects explicitly. That way one can instantly see what data directive accepts.

  2. For easier testing pair the directive with Controller.

Based on the above two suggestions, your directive should look something like this:

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

You would then call above directive like:

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

I suggest you to also read this great blog post which explains "Component Pattern" in details.

Also note that sharing a model by explicitly specifying it when defining an isolated scope is not the only way. Another alternative for sharing a data would for example be a Model as a service (please see related reading).

like image 186
PrimosK Avatar answered Sep 30 '22 19:09

PrimosK


The "good practice" that I would recommend to you is the single responsibility principle.

Every components (directive/controller/service) that you build MUST never do more than one thing. If you avoid this error, your components will be way more reusable, readable and maintanable.

Of course, that practice is not only for angular.

One you respect that, I would recommend you to :

  • Avoid to put all your business code in controllers, and to use instead services (or providers). Services are way more powerfull as they allow to use the angular dependency injection system.

  • Directives should contain only DOM manipulation.

Angular Directive/Controller/Service is kind of a View/ViewModel/Model pattern. Try to keep that in mind.

Edit: Every of your directives can have a controller. You can put directives inside an other directive template and then use the last parameter of the link function (controllers) and the directive require parameter for the communication between directives.

Example: (coffeescript) Let's say i have a container that can be inside others containers itself and may contain also a widget :

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
])
like image 30
antoinestv Avatar answered Sep 30 '22 18:09

antoinestv