I would suggest two options. One simple option is to use events:
<div ng-controller="MyCtrl"> <map></map> <button ng-click="updateMap()">call updateMap()</button> </div> app.directive('map', function() { return { restrict: 'E', replace: true, template: '<div></div>', controller: function(){ $scope.updateMap = function(){ //ajax call here. } }, link: function($scope, element, attrs) { $scope.$on('my.update.map.event', $scope.updateMap); } } }); app.controller('MyCtrl', function ($scope) { $scope.updateMap = function () { $scope.$broadcast('my.update.map.event'); }; });
This isn't a bad solution. You're not polluting the root scope (@Krishna's answer) and your map directive isn't adding an arbitrary value to your controller's scope (@Chandermani's answer).
Another option, if you want to avoid events, is to to use the controllerAs syntax to expose your map directive's controller.
<div ng-controller="MyCtrl"> <map controller="mapController"></map> <button ng-click="mapController.updateMap()">call updateMap()</button> </div> app.directive('map', function() { return { restrict: 'E', replace: true, scope: { 'controller': '=?' }, template: '<div></div>', //controllerAs creates a property named 'controller' on this //directive's scope, which is then exposed by the //'scope' object above controllerAs: 'controller', controller: function(){ this.updateMap = function(){ //ajax call here. } }, link: function($scope, element, attrs, ctrl) { ctrl.updateMap(); } } });
This is similar to @Chandermani's answer, but the coupling between your controller and your directive is much more explicit. I can tell from the view that the map directive is exposing its controller and that it will be called mapController in MyCtrl's scope.
(I found this idea here).