AngularJS directive with method called from outside

22,658

Solution 1

I found my issue.

From the angularJS documentation on directives I was looking into the transclude option since that states:

What does this transclude option do, exactly? transclude makes the contents of a directive with this option have access to the scope outside of the directive rather than inside.

I combined transclude=false with the controller function since that exposes the method, again from docs:

Savvy readers may be wondering what the difference is between link and controller. The basic difference is that controller can expose an API, and link functions can interact with controllers using require.

However what I missed completely was that I isolated scope within my directive. From docs:

What we want to be able to do is separate the scope inside a directive from the scope outside, and then map the outer scope to a directive's inner scope. We can do this by creating what we call an isolate scope. To do this, we can use a directive's scope option:

So even if you use transclude=false and the controller function you'll still fail to expose methods if you use isolated scope! Lesson learned!

While figuring out what went wrong I also made a fiddle for better understanding: http://jsfiddle.net/qyBEr/1/

html

<div ng-app="directiveScopeExample">

    <div ng-controller="Ctrl1">

        <p>see if we can trigger a method form the controller that exists in the directive.</p>

        <ul>
            <li><a href="#" ng-click="methodInController()">Method in Controller</a></li>
            <li><a href="#" ng-click="methodInDirective()">Method in Directive</a></li>
        </ul>

    <simple-directive/>
    </div>
</div>

javascript

angular.module('directiveScopeExample', [])

  .controller('Ctrl1', function Ctrl1($scope) {

      $scope.methodInController = function(){
          alert('Method in controller triggered');
    };

  })
  .directive('simpleDirective', function(){

      return {
      restrict: 'E',
      transclude: false,

      controller: function($scope){
          $scope.methodInDirective = function(){
              // call a method that is defined on scope but only within the directive, this is exposed beause defined within the link function on the $scope
              $scope.showMessage('Method in directive triggered');
          }
      }
      // this is the issue, creating a new scope prevents the controller to call the methods from the directive
      //, scope: {
      //  title: '@'
      //}
      , link: function(scope, element, attrs, tabsCtrl) {
         // view related code here
          scope.showMessage = function(message){
              alert(message);
          }
      },
      //templateUrl: 'some-template-here.html'
     };
  })  

Solution 2

Calling private methods inside directive's link function is very simple

   dropOffScope = $('#drop_off_date').scope();
   dropOffScope.setMinDate('11/10/2014');

where

   $('#drop_off_date') - jQuery function
   setMinDate() - private function inside directive

You can call directive function even from outer space.

Solution 3

By default the scope on directive is false meaning directive will use the parent's scope instead of creating a new one. And hence any function or model defined in the directive will be accessible in the parent scope. Check out this.

I think your problem can be solved as follows:

angular.module('directiveScopeExample', [])

  .controller('Ctrl1', function Ctrl1($scope) {

      $scope.methodInController = function(){
          alert('Method in controller triggered');
    };

  })
  .directive('simpleDirective', function(){

      return {
      restrict: 'E',
      scope: false,
      link: function(scope, element, attrs, tabsCtrl) {
         // view related code here
          scope.showMessage = function(message){
              alert(message);
          }
      },
      //templateUrl: 'some-template-here.html'
     };

This approach might be an issue in case you want to create reusable directives and you are maintaining some state/models in your directive scope. But since you are just creating functions without side-effects, you should be fine.

Share:
22,658
hcpl
Author by

hcpl

Professional Java Software Engineer and Android Developer with a passion for Photography

Updated on November 03, 2020

Comments

  • hcpl
    hcpl over 3 years

    I created a directive with a method that should be called from other elements that are not part of the directive. However it looks like this method is not exposed.

    Some example jade code to clarify:

    //- a controller for the view itself
    div(ng-controller="someController")
    
        //- this is part of the view itself, not within the directive
        div(ng-repeat="element in elements") 
            div(ng-click="methodFromDirective(element)") click element {{$index}} to trigger directive
    
        //- this is the directive
        div(some-directive)
    

    The someController isn't too important here I think. It has methods but NOT the methodFromDirective(element) one. The methodFromDirective(element) is a method that exists only in the directive.

    If I make a directive and put some logging on creation I can clearly see it's created. However the methodFromDirective(element) method isn't exposed so the calls aren't properly triggered.

    The methodFromDirective(element) itself will only work on elements from within the directive's template.

    some coffeescript to show the definition of the the directive (ignore indentation errors here):

    'use strict'
    define [], () ->
    
    someDirective = () ->
        restrict: 'A'
        scope: {
            show: '='
        }
        transclude: false
        templateUrl: 'someTemplateHere.html'
    
        controller = ($scope)  ->
    
           # exposing the method here
           $scope.methodFromDirective(element)->
               $scope.theMethod element
    
        link = (scope, element, attr) ->
    
           # this is logged
           console.log "init someDirective"
    
           # triggering this method form outside fails
           scope.theMethod = (element)->
              console.log "method triggered with element", JSON.stringify(element)
    
  • stephen776
    stephen776 about 9 years
    is there a way to call the directive methods from exising JS? i would like to just be able to call methodInDirective() from any JS on the page
  • enpenax
    enpenax almost 9 years
    Just making sure: the reason why this works is because the directive uses the parent's scope as you don't define an isolated scope. You do not even need the controller declaration in the directive and can move the definition of methodInDirective straight to the link function, too. So basically your directive adds methods to the parent's scope. not sure how that is advisable tho :/
  • The Red Pea
    The Red Pea over 7 years
    "even from outer space"
  • The Red Pea
    The Red Pea over 7 years
    I don't see how transclude is relevant here. This example works whether transclude is true or false, right?
  • 695Multimedia
    695Multimedia over 7 years
    I get "scope is not a function" on my jQuery object.
  • Robert Henderson
    Robert Henderson about 7 years
    I would recommend only using this strategy if you are using a third-party directive that you cannot edit. Otherwise, you should edit your directive to make accessing its methods available elsewhere. If you use this strategy, you should use angular.element(), not the jQuery $() selector.
  • Somnium
    Somnium about 5 years
    This works only in debug mode, so you cannot use this.