I have a directive, here is the code :
.directive('map', function() {
return {
restrict: 'E',
replace: true,
template: '<div></div>',
link: function($scope, element, attrs) {
var center = new google.maps.LatLng(50.1, 14.4);
$scope.map_options = {
zoom: 14,
center: center,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
// create map
var map = new google.maps.Map(document.getElementById(attrs.id), $scope.map_options);
var dirService= new google.maps.DirectionsService();
var dirRenderer= new google.maps.DirectionsRenderer()
var showDirections = function(dirResult, dirStatus) {
if (dirStatus != google.maps.DirectionsStatus.OK) {
alert('Directions failed: ' + dirStatus);
return;
}
// Show directions
dirRenderer.setMap(map);
//$scope.dirRenderer.setPanel(Demo.dirContainer);
dirRenderer.setDirections(dirResult);
};
// Watch
var updateMap = function(){
dirService.route($scope.dirRequest, showDirections);
};
$scope.$watch('dirRequest.origin', updateMap);
google.maps.event.addListener(map, 'zoom_changed', function() {
$scope.map_options.zoom = map.getZoom();
});
dirService.route($scope.dirRequest, showDirections);
}
}
})
I would like to call updateMap()
on a user action. The action button is not on the directive.
What is the best way to call updateMap()
from a controller?
To be honest, I was not really convinced with any of the answers in this thread. So, here's are my solutions:
Directive Handler(Manager) Approach
This method is agnostic to whether the directive's
$scope
is a shared one or isolated oneA
factory
to register the directive instancesThe directive code, I usually put all the logic that doesn't deal with DOM inside directive controller. And registering the controller instance inside our handler
template code
Access the controller instance using the
factory
& run the publicly exposed methodsAngular's approach
Taking a leaf out of angular's book on how they deal with
using $parse and registering controller on
$parent
scope. This technique doesn't work on isolated$scope
directives.Access it inside controller using
$scope.foo
Building on Oliver's answer - you might not always need to access a directive's inner methods, and in those cases you probably don't want to have to create a blank object and add a
control
attr to the directive just to prevent it from throwing an error (cannot set property 'takeTablet' of undefined
).You also might want to use the method in other places within the directive.
I would add a check to make sure
scope.control
exists, and set methods to it in a similar fashion to the revealing module patternAssuming that the action button uses the same controller
$scope
as the directive, just define functionupdateMap
on$scope
inside the link function. Your controller can then call that function when the action button is clicked.fiddle
As per @FlorianF's comment, if the directive uses an isolated scope, things are more complicated. Here's one way to make it work: add a
set-fn
attribute to themap
directive which will register the directive function with the controller:fiddle
A bit late, but this is a solution with the isolated scope and "events" to call a function in the directive. This solution is inspired by this SO post by satchmorun and adds a module and an API.
Create an API to communicate with the directive. The addUpdateEvent adds an event to the event array and updateMap calls every event function.
(Maybe you have to add functionality to remove event.)
In the directive set a reference to the MapAPI and add $scope.updateMap as an event when MapApi.updateMap is called.
In the "main" controller add a reference to the MapApi and just call MapApi.updateMap() to update the map.
Just use scope.$parent to associate function called to directive function
in HTML
Although it might be tempting to expose an object on the isolated scope of a directive to facilitate communicating with it, doing can lead to confusing "spaghetti" code, especially if you need to chain this communication through a couple levels (controller, to directive, to nested directive, etc.)
We originally went down this path but after some more research found that it made more sense and resulted in both more maintainable and readable code to expose events and properties that a directive will use for communication via a service then using $watch on that service's properties in the directive or any other controls that would need to react to those changes for communication.
This abstraction works very nicely with AngularJS's dependency injection framework as you can inject the service into any items that need to react to those events. If you look at the Angular.js file, you'll see that the directives in there also use services and $watch in this manner, they don't expose events over the isolated scope.
Lastly, in the case that you need to communicate between directives that are dependent on one another, I would recommend sharing a controller between those directives as the means of communication.
AngularJS's Wiki for Best Practices also mentions this: