There is another option besides emitting events or wrapping directives into a parent controller (nothing wrong with those options btw). The other option is to have a generic service/factory where you can register arbitrary directive controllers, and then use those registered controllers in other related or non related directives.
Below has a service called directiveCommunicator
where you can get, set and unset directive controllers (you can use a factory if you want, its just my preference to use services). We then have another 2 directives called foo
and bar
, which the foo
directives registers its controller to be used, which in turn is used by the bar
directive. Note that foo
and bar
directives are not parent/child related
// Service used to register/use any arbitrary controller
app.service('directiveCommunicator',
function()
{
var _controllers = {};
this.get =
function(id)
{
if (!(id in _controllers)) {
return null;
}
return _controllers[id];
};
this.set =
function(id, controller)
{
_controllers[id] = controller;
};
this.unset =
function(id)
{
if (!(id in _controllers)) {
return;
}
delete _controllers[i];
}
}
);
app.directive('foo',
[
'directiveCommunicator',
function(directiveCommunicator)
{
return {
'restrict': 'A',
'scope':
{
'colour': '='
},
'controller':
function($scope)
{
// We register out controller with a unique ID so we can use it in other directives
directiveCommunicator.set('colourBox', this);
// We also unregister it once we get destroyed, otherwise we'll be leaking memory
$scope.$on('$destroy',
function()
{
directiveCommunicator.unset('colourBox');
}
);
this.changeColour =
function(colour)
{
$scope.$apply(
function()
{
$scope._colour = colour;
}
);
}
},
'link':
function($scope, $element, $attr)
{
$scope._colour = $attr.colour;
$scope.$watch('_colour',
function()
{
$element.attr('class', $scope._colour);
}
);
}
}
}
]
);
app.directive('bar',
[
'directiveCommunicator',
function(directiveCommunicator)
{
return {
'restrict': 'A',
'scope':
{
'colour': '='
},
'link':
function($scope, $element, $attr)
{
$element.text($attr.colour);
$element.bind('click',
function()
{
// We get the registered controller and call the 'changeColour' method on it
var ctrl = directiveCommunicator.get('colourBox');
ctrl.changeColour($attr.colour);
}
);
}
}
}
]
);
I've made a little Plunker demo to see foo
and bar
in action. The foo
directive is just a little square div where you can change the background in the associated controller. bar
is another non related directive that will call foo
's controller method changeColour
and change the colour based on the supplied attributes.
Haven't used this setup in production yet and you also need to handle unregistering controllers, but should work. You could also have a 3rd argument in directiveCommunicator.set
method being the controller's scope, which the method will automatically add in the $destroy/unregister setup, therefore not having to call directiveCommunicator.unset
anymore