1

I have multiple angularJS directives (that use one of the angular versions of chart.js)

Now I have a couple of functions that I need to use in these directives.

What is a good way to make me not repeat myself, so remove the code from the directives and have it in one place. Since that code is identical anyways. I've looked into inheritance of scopes but haven't been able to solve this problem yet.

This is the code that is used in multiple directives:

        $scope.widgetData = false;
        $scope.graphData = false;
        $scope.graphSelectorIndex = 0;

        $scope.graphSelector = [
            { 'byPeriod' : 'Periode'},
            { 'byHour' : 'Uur' },
            { 'byDay' : 'Dag'}
        ];

        $scope.graphSelectorByText = function (text) {
            switch (text) {
                case ('byPeriod'):
                    $scope.selector = 'byPeriod'
                    $scope.graphData = $scope.allGraphData.byPeriod;
                    $scope.graphType = 'Line';
                    break;
                case ('byDay'):
                    $scope.selector = 'byDay'
                    $scope.graphData = $scope.allGraphData.byDay;
                    $scope.graphType = 'Line';
                    break;
                case ('byHour'):
                    $scope.selector = 'byHour'
                    $scope.graphData = $scope.allGraphData.byHour;
                    $scope.graphType = 'Bar';
                    break;
            }
        }

        $scope.graphSelectorByInt = function (int) {
            switch (int) {
                case (0):
                    $scope.selector = 'byPeriod';
                    $scope.graphData = $scope.allGraphData.byPeriod;
                    $scope.graphType = 'Line';
                    break;
                case (1):
                    $scope.selector = 'byDay';
                    $scope.graphData = $scope.allGraphData.byDay;
                    $scope.graphType = 'Line';
                    break;
                case (2):
                    $scope.selector = 'byHour';
                    $scope.graphData = $scope.allGraphData.byHour;
                    $scope.graphType = 'Bar'
                    break;
            }
        }

        $scope.graphSelectorPrev = function () {
            $scope.graphSelectorIndex--;
            if ($scope.graphSelectorIndex < 0) {
                $scope.graphSelectorIndex = $scope.graphSelector.length-1;
            }
            $scope.graphSelectorByInt($scope.graphSelectorIndex);
            console.log($scope.graphSelectorIndex);

        }

        $scope.graphSelectorNext = function () {
            $scope.graphSelectorIndex++;
            if ($scope.graphSelectorIndex >= $scope.graphSelector.length) {
                $scope.graphSelectorIndex = 0;
            }
            $scope.graphSelectorByInt($scope.graphSelectorIndex);
            console.log($scope.graphSelectorIndex);
        }

Some html:

    <div class="controls">
        <span class="btn_arrow previous inactive" ng-click="graphSelectorPrev()">Vorige</span>
        <p>{+ selector +}</p>
        <span class="btn_arrow next" ng-click="graphSelectorNext()">Volgende</span>
    </div>

Thanks to anyone that can help!

5
  • Maybe I don't get you right, but the only dublicate is the stuff in the case structure? Commented Dec 22, 2014 at 9:26
  • You should use object: one of them has keys like 'byPeriod' etc. with function as value. When you need find function by int you should do something like that var key = Object.keys(object)[int]; object(key); Commented Dec 22, 2014 at 9:40
  • @semptic The functions I've listed are used in multiple directives, so how would I get this into a central place. Commented Dec 22, 2014 at 9:56
  • Interesting would be the difference between the directives. Why not using the same directive everywhere? Commented Dec 22, 2014 at 10:10
  • The templates and the data received is different. Commented Dec 22, 2014 at 12:52

4 Answers 4

1

First of all, in this case you can make some DRY this way:

HTML:

<div class="controlls"> <span class="btn_arrow previous inactive" ng-click="graphSelectorInc(-1)">Vorige</span>

    <p>{{ selector }}</p> <span class="btn_arrow next" ng-click="graphSelectorInc(+1)">Volgende</span>

</div>

JS:

//Warning this approach is so called not monomorh(polymorph): the parameter of function is not strictly typed, so it lacks browser optimizations but in this case its not so important because code won't run often
$scope.graphSelectorByAny = function (step) {
    switch (step) {
        case ('byPeriod'):
        case (0):
            $scope.selector = 'byPeriod'
            $scope.graphData = $scope.allGraphData.byPeriod;
            $scope.graphType = 'Line';
            break;
        case ('byDay'):
        case (1):
            $scope.selector = 'byDay'
            $scope.graphData = $scope.allGraphData.byDay;
            $scope.graphType = 'Line';
            break;
        case ('byHour'):
        case (2):
            $scope.selector = 'byHour'
            $scope.graphData = $scope.allGraphData.byHour;
            $scope.graphType = 'Bar';
            break;
    }
}

$scope.graphSelectorInc = function (inc) {
    $scope.graphSelectorIndex += inc;
    if ($scope.graphSelectorIndex < 0) {
        $scope.graphSelectorIndex = $scope.graphSelector.length - 1;
    } else if ($scope.graphSelectorIndex >= $scope.graphSelector.length) {
        $scope.graphSelectorIndex = 0;
    }
    $scope.graphSelectorByAny($scope.graphSelectorIndex);
    console.log($scope.graphSelectorIndex);

}

I've made a JSFiddle for you.

But it cound be also good to have a separate diretive, I cant's write cause I don't know what data do you exectly want to be in the controller. Yo can read about directives and data binding from the official docs.

Also consider using services whem you want multiply controllers/directives to do same things.

Hope it will help you.

Sign up to request clarification or add additional context in comments.

1 Comment

Just tested your code, this is already a lot better, thanks! I was thinking about having this code in a directive yes.
1

Without knowing the exact difference between the directives (maybe you don't even need multiple) I would suggest to use a service or simply create a controller and add it to the directive template.

I would only go for the services if you get some benefits of sharing the state between the directives, otherwise I would go for the controller.

Here is a short example what I mean by 'using a controller' (test on jsfiddle):

angular.module('myApp', [])
.controller('sharedCtrl', function() {
    var self = this;

    // $scope.doIt = ...
    self.doIt = function (msg) {
      alert(msg);
    };
})
.directive('dir1', function () {
    return {
        restrict: 'E',
        template: '<div ng-controller="sharedCtrl as ctrl"><button ng-click="ctrl.doIt(msg)">dir1</button></div>',
        scope: {},
        link: function (scope) {
            scope.msg = 'Hi, I\'m dir1!';
        }
    };
})
.directive('dir2', function () {
    return {
        restrict: 'E',
        template: '<div ng-controller="sharedCtrl as ctrl"><button ng-click="ctrl.doIt(msg)">dir2</button></div>',
        scope: {},
        link: function (scope) {
            scope.msg = 'Hi, I\'m dir2!';
        }
    };
});

If you need to pass stuff from the directive scope you can pass it simply to the controller functions.

P.S.: I'm prefer the controller as syntax instead of using $scope. If you aren't familiar with it you can replace self with $scope, but I suggest to looking further into controller as.

Comments

0

Personally I love services approach.

See: http://jsfiddle.net/elennaro/b48jex3w/33/

Here is the code:

HTML

<div ng-controller="BaseCtrl">
<div class="controlls"> <span class="btn_arrow previous inactive" ng-click="graphSelectorInc(-1)">Vorige</span>

    <p>{{ selector }}</p> <span class="btn_arrow next" ng-click="graphSelectorInc(+1)">Volgende</span>

</div>

JS

angular.module('myApp', [])
.service('GraphSelector', function () {
var graphSelectorIndex = 0;
var graphSelector = [{
    'byPeriod': 'Periode'
}, {
    'byHour': 'Uur'
}, {
    'byDay': 'Dag'
}];
return {
    selector: this.selector,
    inc: function (inc) {
        graphSelectorIndex += inc;
        if (graphSelectorIndex < 0) {
            graphSelectorIndex = graphSelector.length - 1;
        } else if (graphSelectorIndex >= graphSelector.length) {
            graphSelectorIndex = 0;
        }
        console.log(Object.keys(graphSelector[graphSelectorIndex]));
        return graphSelector[graphSelectorIndex][Object.keys(graphSelector[graphSelectorIndex])[0]];
    }

}
}).controller('BaseCtrl', function ($scope, GraphSelector) {

$scope.graphSelectorInc = function (inc) {
    $scope.selector = GraphSelector.inc(inc);

}});

You see I have a service GraphSelector that is a singleton and it stores graphSelectorIndex and graphSelector same for all controllers/directives whoever will call him.

So you can call this service from any place you want to switch purrent selector properties.

I use Object.keys to get only the Value of current selector state returned to our controller. But if you want you can return the whole object and get the value in controller.

Angular JS services is a powerful tool to get DRY clean and readable code, also I use it often to share some data between controllers.

1 Comment

I really like this. I'm sure I will look back at this code for reference, thanks a lot for this! =)
0

You could create a shared controller containing the re-usable logic and then dynamically link it to you directive.

So your controller would be:

app.controller('sharedCtrl', function($scope) {
    $scope.widgetData = false;
    $scope.graphData = false;
    $scope.graphSelectorIndex = 0;

    $scope.graphSelector = [
        { 'byPeriod' : 'Periode'},
        { 'byHour' : 'Uur' },
        { 'byDay' : 'Dag'}
    ];

    $scope.graphSelectorByText = function (text) {
        switch (text) {
            case ('byPeriod'):
                $scope.selector = 'byPeriod'
                $scope.graphData = $scope.allGraphData.byPeriod;
                $scope.graphType = 'Line';
                break;
            case ('byDay'):
                $scope.selector = 'byDay'
                $scope.graphData = $scope.allGraphData.byDay;
                $scope.graphType = 'Line';
                break;
            case ('byHour'):
                $scope.selector = 'byHour'
                $scope.graphData = $scope.allGraphData.byHour;
                $scope.graphType = 'Bar';
                break;
        }
    }

    $scope.graphSelectorByInt = function (int) {
        switch (int) {
            case (0):
                $scope.selector = 'byPeriod';
                $scope.graphData = $scope.allGraphData.byPeriod;
                $scope.graphType = 'Line';
                break;
            case (1):
                $scope.selector = 'byDay';
                $scope.graphData = $scope.allGraphData.byDay;
                $scope.graphType = 'Line';
                break;
            case (2):
                $scope.selector = 'byHour';
                $scope.graphData = $scope.allGraphData.byHour;
                $scope.graphType = 'Bar'
                break;
        }
    }

    $scope.graphSelectorPrev = function () {
        $scope.graphSelectorIndex--;
        if ($scope.graphSelectorIndex < 0) {
            $scope.graphSelectorIndex = $scope.graphSelector.length-1;
        }
        $scope.graphSelectorByInt($scope.graphSelectorIndex);
        console.log($scope.graphSelectorIndex);

    }

    $scope.graphSelectorNext = function () {
        $scope.graphSelectorIndex++;
        if ($scope.graphSelectorIndex >= $scope.graphSelector.length) {
            $scope.graphSelectorIndex = 0;
        }
        $scope.graphSelectorByInt($scope.graphSelectorIndex);
        console.log($scope.graphSelectorIndex);
    }

});

And your directives would look like this:

app.directive('myDirective', function() {
    return {
         restrict: 'E',
         controller: '@',
         name: 'myDirectiveCtrl',
         link: function($scope, $element, $attrs) {
             // add directive specific logic
         }
    }
});

Then the markup looks like this:

<my-directive my-directive-ctrl="sharedCtrl"></my-directive>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.