I'd make use of the following:
- A constant, to hold which types of charts you support throughout the application.
- A named controller for each type of chart.
- (Optional) A service to be aware of the currently active charts and what type they are.
Register the app
var app = angular.module('app', []);
Register the constant to control which types we support
app.constant('chartTypes', ['typeOne', 'typeTwo']);
Directive
app.directive('chart', ['$controller', 'chartTypes', function ($controller, chartTypes) {
return {
restrict: 'E',
scope: {
'showGrouped': '='
},
controller: function ($scope, $element, $attrs) {
if (chartTypes.indexOf($attrs.type) === -1) {
throw new Error('Chart type ' + $attrs.type + ' is not supported at the moment');
} else {
return $controller($attrs.type + 'Controller', {
$scope: $scope,
$attrs: $attrs
});
}
},
link: function (scope, element, attrs, controller) {
// Rock'n'roll...
controller.init();
}
}
}]);
Note: I stripped away the ngModel requirement from the directive for now to make it clearer
how to build something like this. I'm not sure how you would get ngModel to play alongside this solution,
but I'm sure you can figure that one out.. If not, I'd be happy to give it a try later on.
Controller(s)
app.controller('typeOneController', ['$scope', '$attrs', function ($scope, $attrs) {
var ctrl = this;
ctrl.init = function () {
console.log('We are initialised and ready to rock!');
};
$scope.someFunc = function () {
console.log($scope, 'is the isolate scope defined in the directive');
};
}]);
Markup:
<chart type="typeOne" legend="true"></chart>
<chart type="typeTwo" legend="true"></chart>
<chart type="typeThree" legend="true"></chart>
Expected result:
- Chart
typeOne
should be rolling fine at this point, logging out that we are in fact initialised.
- Chart
typeTwo
should throw an error stating that a Controller by that name could not be found (undefined).
- Chart
typeThree
should throw an error stating that the passed in chartType is not currently supported.
In closing:
Now, this is not your conventional directive structure - but it's one I think is highly underused.
The benefits of having your linking function be a controller, is that you can completely separate
the $scope behaviour from the directive definition.
This in turn allows us to unit test $scope behaviour for the directive itself, without
the need to instantiate the directive and its DOM structure in our unit tests. Another added
benefit is that you don't have to setup multiple directives for different chart types, we simply call for a controller (or, linking behaviour (if you will))
based on the chart type passed into the directive definition.
This could be built on further to include services and what not, everything can then be injected into your Controllers, on a per-chart basis (or into the directive definition, to have it be there for all the charts), giving you a ton of flexibility and ease of testing to boot.
Another lesson to take home, is that anonymous functions as directive controllers are hard to test, in comparison to named controllers that are defined elsewhere, then injected into the directive. Separation is gold.
Let me know if this does the trick for you, or if you need a more in depth example of how to set it up.
I'll see if I can't get a plunker or something of the sort uploaded throughout the day.
Edit:
Added jsFiddle showcasing the behaviour, albeit a bit simplified (not an isolated scope, no templateUrl):
http://jsfiddle.net/ADukg/5416/
Edit 2: Updated the jsFiddle link, this one was with an isolated scope and hence the ng-click definition on the calling element does not fire off the isolated functions.
Edit 3: Added example of some $scope functions.
Edit 4: Added another jsFiddle, showcasing an isolate scope and a templateURL. More in line with what you are working with.
http://jsfiddle.net/ADukg/5417/