Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS directive with method called from outside

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)
like image 878
hcpl Avatar asked Jan 18 '14 10:01

hcpl


3 Answers

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'
     };
  })  
like image 185
hcpl Avatar answered Nov 07 '22 10:11

hcpl


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.

like image 45
Aivils Štoss Avatar answered Nov 07 '22 10:11

Aivils Štoss


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.

like image 20
user3452789 Avatar answered Nov 07 '22 11:11

user3452789