From the angular.js documentation on directives
When a directive uses require
, $compile
will throw an error unless the specified controller is found. The ^
prefix means that this directive searches for the controller on its parents (without the ^
prefix, the directive would look for the controller on just its own element).
So basically what you are trying to do with having siblings directly communicate is not possible. I had run into this same issue but I did not want to use a service for communication. What I came up with was a method of using a parent directive to manage communication between its children, which are siblings. I posted the example on github.
What happens is that both children require the parent (require: '^parentDirective'
) and their own controller, both of which are passed into the link function. From there each child can get a reference to the parent controller and all of its public methods, as an API of sorts.
Below is one of the children itemEditor
function itemEditor() {
var directive = {
link: link,
scope: {},
controller: controller,
controllerAs: 'vm',
require: ['^itemManager', 'itemEditor'],
templateUrl: 'app/scripts/itemManager/itemManager.directives.itemEditor.html',
restrict: 'A'
};
return directive;
function link(scope, element, attrs, controllers) {
var itemManagerController = controllers[0];
var itemEditorController = controllers[1];
itemEditorController.itemManager = itemManagerController;
itemEditorController.initialize();
}
function controller() {
var vm = this;
// Properties
vm.itemManager = {};
vm.item = { id: -1, name: "", size: "" };
// Methods
vm.initialize = initialize;
vm.updateItem = updateItem;
vm.editItem = editItem;
// Functions
function initialize() {
vm.itemManager.respondToEditsWith(vm.editItem);
}
function updateItem() {
vm.itemManager.updateItem(vm.item);
vm.item = {};
}
function editItem(item) {
vm.item.id = item.id;
vm.item.name = item.name;
vm.item.size = item.size;
}
}
}
Note how the values passed into the require
array are the parent directive's name and the current directive's name. These are then both accessible in the link
function via the controllers
parameter. Assign the parent directive's controller as a property of the current child's and then it can be accessed within the child's controller functions via that property.
Also notice how in the child directive's link
function I call an initialize
function from the child's controller. This is where part of the communication lines are established.
I'm basically saying, anytime you (parent directive) receive a request to edit an item, use this method of mine named editItem
which takes an item
as a parameter.
Here is the parent directive
function itemManager() {
var directive = {
link: link,
controller: controller,
controllerAs: 'vm',
templateUrl: 'app/scripts/itemManager/itemManager.directives.itemManager.html',
restrict: 'A'
};
return directive;
function link(scope, element, attrs, controller) {
}
function controller() {
var vm = this;
vm.updateMethod = null;
vm.editMethod = null;
vm.updateItem = updateItem;
vm.editItem = editItem;
vm.respondToUpdatesWith = respondToUpdatesWith;
vm.respondToEditsWith = respondToEditsWith;
function updateItem(item) {
vm.updateMethod(item);
}
function editItem(item) {
vm.editMethod(item);
}
function respondToUpdatesWith(method) {
vm.updateMethod = method;
}
function respondToEditsWith(method) {
vm.editMethod = method;
}
}
}
Here in the parent you can see that the respondToEditsWith
takes a method as a parameter and assigns that value to its editMethod
property. This property is called whenever the controller's editItem
method is called and the item
object is passed on to it, thus calling the child directive's editItem
method. Likewise, saving data works the same way in reverse.
Update: By the way, here is a blog post on coderwall.com where I got the original idea with good examples of require
and controller options in directives. That said, his recommended syntax for the last example in that post did not work for me, which is why I created the example I reference above.