2

I want to call a function defined in a directive, just the opposite of this stackoverflow question

I tried this but does not work.

app.directive('myDirective', function() {
    return {
        link: function(scope, element, attrs) {
            scope.someDirectiveFn = function(arg) {
                 return "in directive";
            };
        },
    }
});

function MyCtrl($scope) {
    alert($scope.someDirectiveFn());
}

Is that possible? how could I get it? is it a bad practice?

EDIT

I got this way:

.controller('MyCtrl', function($scope) {
    alert($scope.func());
})

.directive('myDirective', function() {
    return {
      controller: function($scope, $element){
        $scope.func = function() {
          return "text";
         };
      }
    }
});
Community
  • 1
  • 1
Andres
  • 4,323
  • 7
  • 39
  • 53

3 Answers3

3

You can use event system to achieve that.

First, emit a custom event on your scope with your parameters. Second, listen to the scope in your directive with angular $on method.

app.controller('MyCtrl', function($scope) {
  $scope.invokeDirectiveMethod = function() {
    $scope.$emit('invokeMyDirectiveMethod', 'myParameter');
  };
})

.directive('myDirective', function() {
  return {
    link: function(scope, element, attrs) {
      var someDirectiveFn = function(event, arg) {
        alert(arg + " in directive");
      };

      scope.$on('invokeMyDirectiveMethod', someDirectiveFn);
    },
  }
});

Here is a working plunker.

UPDATE

Based on your update, event communication does not fit your problem.

How about passing an object to directive using two way binding and defining someDirectiveFn in that object? This way you can pass arguments and return values from it.

app.controller('MyCtrl', function($scope) {
  $scope.shareObject = {};

  $scope.invokeDirectiveMethod = function() {
    if (angular.isFunction($scope.shareObject.someDirectiveFn)) {
      $scope.message = $scope.shareObject.someDirectiveFn('from controller'); 
    }
  };
})

.directive('myDirective', function() {
  return {
    scope: {
      'shareObject': '='
    },
    link: function(scope, element, attrs) {
      scope.shareObject.someDirectiveFn = function(arg) {
        return arg + ' from parameter';
      };
    },
  }
});

Updated plunker.

halilb
  • 4,055
  • 1
  • 23
  • 31
  • $emit is nice, but it can not help me when I need to return a value from directive. I edited my question to explain better. – Andres Nov 07 '14 at 17:09
  • It works, thank you very much. I got another approach(check question), could you tell me what you think about it? what's better? best practice? – Andres Nov 07 '14 at 17:37
  • well, this is not the best way to communicate actually. But i don't know if you really have to call a directive method? What is your actual use case for this scenario? I might suggest a better way to if you provide it. – halilb Nov 07 '14 at 17:40
  • Right, that's exactly what I meant. You have to ask yourself one simple question: Do I want to modify the directive with that function in any way? - If not, your design is wrong. Your method shouldnt be in the directive then. – Jonas Nov 07 '14 at 17:51
1

You didnt post your html code, so I assume that your custom directive is used IN MyCtrl. This does not work because of when the functions are executed.

Controller functions are always executed before link functions.

Here is a cool article about the differences.

So if you want your controller to be able to invoke a function in the directive you can broadcast events (like in halilb's answer) or make the directive to listen to specific scope values, like this:

app.directive('myDirective', function() {
    return {
        link: function(scope, element, attrs) {
            scope.$watch("message", function() { console.log(message); });
        },
    }
});

function MyCtrl($scope) {
    $scope.message = "Hello Directive";
}
Jonas
  • 1,315
  • 1
  • 18
  • 31
  • Good article but I need directive to return some value. I edited my question to explain better. – Andres Nov 07 '14 at 17:11
  • @Andres - Well, why you want to add that function to the directive then? You would only place it in the link function of the directive if you want to manipulate the directives DOM from your controller. Otherwise, just define the function in the controller. – Jonas Nov 07 '14 at 17:14
  • Do you mean to define it in the directive's controller? something like http://plnkr.co/edit/GzJ8LIbhLh17XW9e2XYT?p=preview ?? – Andres Nov 07 '14 at 17:23
  • @Andres - No, I mean. For what reason do you use the directive at all? If the return value does not affect the directive, then it is useless to define the method IN the directive. Just do the calculation in your `MyCtrl` – Jonas Nov 07 '14 at 17:29
0

I'm guessing you want to have a function that is shared between a directive and a controller lets say?

if thats the case how about creating a service and injecting the service into both directive and ctrl. This way you would only have to create it once.

  app.service('sharedFucntionService', function() {
     return {
        someFn : function (arg) {
             alert(arg + " in directive")  
        }
    }
 });

Injecting the service into a directive

 app.directive('myDirective',function( sharedFucntionService){
  return {
        link: function (scope, element, attrs) {
            // do something with sharedFucntionService
        }
    }

});

Inject the service into the controller as well function MyCtrl($scope, sharedFucntionService){ ...... }

ssayyed
  • 766
  • 1
  • 8
  • 13