Rectangle 27 37

Accessing controller method means accessing a method on parent scope from directive controller/link/scope.

If the directive is sharing/inheriting the parent scope then it is quite straight forward to just invoke a parent scope method.

Little more work is required when you want to access parent scope method from Isolated directive scope.

There are few options (may be more than listed below) to invoke a parent scope method from isolated directives scope or watch parent scope variables (option#6 specially).

Note that I used link function in these examples but you can use a directive controller as well based on requirement.

Through Object literal and from directive html template

Option#2. Through Object literal and from directive link/scope

Option#3. Through Function reference and from directive html template

Option#4. Through Function reference and from directive link/scope

Option#5: Through ng-model and two way binding, you can update parent scope variables.

index.html
itemfilterTemplate.html
<select ng-model="selectedItems" multiple="multiple" style="height: 200px; width: 250px;" 
 ng-options="item.id as item.name group by item.model for item in items | orderBy:'name'">
  <option>--</option>
</select>
app.js

Option#6: Through $watch and $watchCollection It is two way binding for items in all above examples, if items are modified in parent scope, items in directive would also reflect the changes.

If you want to watch other attributes or objects from parent scope, you can do that using $watch and $watchCollection as given below

<!DOCTYPE html>
<html ng-app="plunker">

<head>
  <meta charset="utf-8" />
  <title>AngularJS Plunker</title>
  <script>
    document.write('<base href="' + document.location + '" />');
  </script>
  <link rel="stylesheet" href="style.css" />
  <script data-require="angular.js@1.3.x" src="https://code.angularjs.org/1.3.9/angular.js" data-semver="1.3.9"></script>
  <script src="app.js"></script>
</head>

<body ng-controller="MainCtrl">
  <p>Hello {{user}}!</p>
  <p>directive is watching name and current item</p>
  <table>
    <tr>
      <td>Id:</td>
      <td>
        <input type="text" ng-model="id" />
      </td>
    </tr>
    <tr>
      <td>Name:</td>
      <td>
        <input type="text" ng-model="name" />
      </td>
    </tr>
    <tr>
      <td>Model:</td>
      <td>
        <input type="text" ng-model="model" />
      </td>
    </tr>
  </table>

  <button style="margin-left:50px" type="buttun" ng-click="addItem()">Add Item</button>

  <p>Directive Contents</p>
  <sd-items-filter ng-model="selectedItems" current-item="currentItem" name="{{name}}" selected-items-changed="selectedItemsChanged" items="items"></sd-items-filter>

  <P style="color:red">Selected Items (in parent controller) set to: {{selectedItems}}</p>
</body>

</html>
var app = angular.module('plunker', []);

app.directive('sdItemsFilter', function() {
  return {
    restrict: 'E',
    scope: {
      name: '@',
      currentItem: '=',
      items: '=',
      selectedItems: '=ngModel'
    },
    template: '<select ng-model="selectedItems" multiple="multiple" style="height: 140px; width: 250px;"' +
      'ng-options="item.id as item.name group by item.model for item in items | orderBy:\'name\'">' +
      '<option>--</option> </select>',
    link: function(scope, element, attrs) {
      scope.$watchCollection('currentItem', function() {
        console.log(JSON.stringify(scope.currentItem));
      });
      scope.$watch('name', function() {
        console.log(JSON.stringify(scope.name));
      });
    }
  }
})

 app.controller('MainCtrl', function($scope) {
  $scope.user = 'World';

  $scope.addItem = function() {
    $scope.items.push({
      id: $scope.id,
      name: $scope.name,
      model: $scope.model
    });
    $scope.currentItem = {};
    $scope.currentItem.id = $scope.id;
    $scope.currentItem.name = $scope.name;
    $scope.currentItem.model = $scope.model;
  }

  $scope.selectedItems = ["allItems"];

  $scope.items = [{
    "id": "allItems",
    "name": "All Items",
    "order": 0
  }, {
    "id": "CaseItem",
    "name": "Case Item",
    "model": "PredefinedModel"
  }, {
    "id": "Application",
    "name": "Application",
    "model": "Bank"
  }]
});

You can always refer AngularJs documentation for detailed explanations about directives.

This worked creating a wrapper function in the link method. It was only a problem when I passed the scope function down to another directive from within a directive. The original parent controller function was still called but arguments was undefined until I wrapped it like you suggest.

I had to trigger a fetch from the server so the link function example worked really well for me as I didn't even have to pass data back and forth, just call the ajax function. You deserve like 3 votes for such an accurate and well put answer.

calling method of parent controller from a directive in AngularJS - St...

angularjs angularjs-directive
Rectangle 27 2

You should define method with parameter, rather than putting it reference on directive element.

my-callback="test.callbackMethod(itemIndex)"
scope: {
  myCallback: '&myCallback',
  itemIndex: '='
},

angularjs - Unable to call parent controller method from directive - S...

angularjs angularjs-directive angularjs-controller
Rectangle 27 10

You could also expose the directive's controller to the parent scope, like ngForm with name attribute does: http://docs.angularjs.org/api/ng.directive:ngForm

In this example I have myDirective with dedicated controller with $clear method (sort of very simple public API for the directive). I can publish this controller to the parent scope and use call this method outside the directive.

This requires a relationship between the controllers, right? Since OP wanted a message center, this may not be ideal to him. But it was very nice to learn your approach as well. It's useful in many situations and, like you said, angular itself uses it.

I a trying to follow an example provided by satchmorun. I am generating some html at the runtime, but I am not using directive's template. I am using directive's controller to specify a function to call from the added html but the function is not getting called. Basically, I have this directive: directives.directive('abcXyz', function ($compile { return { restrict: 'AE', require: 'ngModel', controller: function ($scope) { $scope.function1 = function () { .. }; }, my html is:"<a href="" ng-click="function1('itemtype')">

This is the only elegant solution which can expose directive api if directive isn't a singleton! I still don't like using $scope.$parent[alias] because it's smells for me like using internal angular api. But still can't find more elegant solution for not-singleton directives. Other variants like broadcasting events or define empty object in parent controller for directive api smells even more.

javascript - Call method in directive controller from other controller...

javascript jquery angularjs angularjs-directive
Rectangle 27 10

You could also expose the directive's controller to the parent scope, like ngForm with name attribute does: http://docs.angularjs.org/api/ng.directive:ngForm

In this example I have myDirective with dedicated controller with $clear method (sort of very simple public API for the directive). I can publish this controller to the parent scope and use call this method outside the directive.

This requires a relationship between the controllers, right? Since OP wanted a message center, this may not be ideal to him. But it was very nice to learn your approach as well. It's useful in many situations and, like you said, angular itself uses it.

I a trying to follow an example provided by satchmorun. I am generating some html at the runtime, but I am not using directive's template. I am using directive's controller to specify a function to call from the added html but the function is not getting called. Basically, I have this directive: directives.directive('abcXyz', function ($compile { return { restrict: 'AE', require: 'ngModel', controller: function ($scope) { $scope.function1 = function () { .. }; }, my html is:"<a href="" ng-click="function1('itemtype')">

This is the only elegant solution which can expose directive api if directive isn't a singleton! I still don't like using $scope.$parent[alias] because it's smells for me like using internal angular api. But still can't find more elegant solution for not-singleton directives. Other variants like broadcasting events or define empty object in parent controller for directive api smells even more.

javascript - Call method in directive controller from other controller...

javascript jquery angularjs angularjs-directive
Rectangle 27 163

In which order the directive functions are executed?

Based on the following plunk, consider the following HTML markup:

<body>
    <div log='some-div'></div>
</body>

With the following directive declaration:

myApp.directive('log', function() {

    return {
        controller: function( $scope, $element, $attrs, $transclude ) {
            console.log( $attrs.log + ' (controller)' );
        },
        compile: function compile( tElement, tAttributes ) {
            console.log( tAttributes.log + ' (compile)'  );
            return {
                pre: function preLink( scope, element, attributes ) {
                    console.log( attributes.log + ' (pre-link)'  );
                },
                post: function postLink( scope, element, attributes ) {
                    console.log( attributes.log + ' (post-link)'  );
                }
            };
         }
     };  

});
some-div (compile)
some-div (controller)
some-div (pre-link)
some-div (post-link)

We can see that compile is executed first, then controller, then pre-link and last is post-link.

Note: The following does not apply to directives that render their children in their link function. Quite a few Angular directives do so (like ngIf, ngRepeat, or any directive with transclude). These directives will natively have their link function called before their child directives compile is called.

The original HTML markup is often made of nested elements, each with its own directive. Like in the following markup (see plunk):

<body>
    <div log='parent'>
        <div log='..first-child'></div>
        <div log='..second-child'></div>
    </div>
</body>

The console output will look like this (without the comments):

// The compile phase
parent (compile)
..first-child (compile)
..second-child (compile)

// The link phase   
parent (controller)
parent (pre-link)
..first-child (controller)
..first-child (pre-link)
..first-child (post-link)
..second-child (controller)
..second-child (pre-link)
..second-child (post-link)
parent (post-link)

We can distinguish two phases here - the compile phase and the link phase.

When the DOM is loaded Angular starts the compile phase, where it traverses the markup top-down, and calls compile on all directives. Graphically, we could express it like so:

It is perhaps important to mention that at this stage, the templates the compile function gets are the source templates (not instance template).

DOM instances are often simply the result of a source template being rendered to the DOM, but they may be created by ng-repeat, or introduced on the fly.

Whenever a new instance of an element with a directive is rendered to the DOM, the link phase starts.

In this phase, Angular calls controller, pre-link, iterates children, and call post-link on all directives, like so:

@lzhaki The flowchart looks nice. Mind to share the charting tool's name? :)

very nice example, plnkr.co/edit/pWJhfgdA2iDzSKV5bjyh?p=preview it has clearly explained all the phase of directive lifecycle.

@merlin I've used OmniGraffle (but could have used illustrator or inkscape - other than speed, there's nothing OmniGraffle does better than other charting tools as far as this illustration is concerned).

@Anant's plunker disappeared so here's a new one: plnkr.co/edit/kZZks8HN0iFIY8ZaKJkA?p=preview Open the JS console to see the log statements

WHY this is not true when ng-repeat is used for children directives??? See plunk:plnkr.co/edit/HcH4r6GV5jAFC3yOZknc?p=preview

@Luckylooke Your plunk has got no children with directive under ng-repeat (ie, what's being repeated is a template with a directive. If it would, you'd see that their compile is only called after the link of ng-repeat.

angularjs - Angular directives - when and how to use compile, controll...

angularjs angularjs-directive
Rectangle 27 161

In which order the directive functions are executed?

Based on the following plunk, consider the following HTML markup:

<body>
    <div log='some-div'></div>
</body>

With the following directive declaration:

myApp.directive('log', function() {

    return {
        controller: function( $scope, $element, $attrs, $transclude ) {
            console.log( $attrs.log + ' (controller)' );
        },
        compile: function compile( tElement, tAttributes ) {
            console.log( tAttributes.log + ' (compile)'  );
            return {
                pre: function preLink( scope, element, attributes ) {
                    console.log( attributes.log + ' (pre-link)'  );
                },
                post: function postLink( scope, element, attributes ) {
                    console.log( attributes.log + ' (post-link)'  );
                }
            };
         }
     };  

});
some-div (compile)
some-div (controller)
some-div (pre-link)
some-div (post-link)

We can see that compile is executed first, then controller, then pre-link and last is post-link.

Note: The following does not apply to directives that render their children in their link function. Quite a few Angular directives do so (like ngIf, ngRepeat, or any directive with transclude). These directives will natively have their link function called before their child directives compile is called.

The original HTML markup is often made of nested elements, each with its own directive. Like in the following markup (see plunk):

<body>
    <div log='parent'>
        <div log='..first-child'></div>
        <div log='..second-child'></div>
    </div>
</body>
// The compile phase
parent (compile)
..first-child (compile)
..second-child (compile)

// The link phase   
parent (controller)
parent (pre-link)
..first-child (controller)
..first-child (pre-link)
..first-child (post-link)
..second-child (controller)
..second-child (pre-link)
..second-child (post-link)
parent (post-link)

We can distinguish two phases here - the compile phase and the link phase.

When the DOM is loaded Angular starts the compile phase, where it traverses the markup top-down, and calls compile on all directives. Graphically, we could express it like so:

It is perhaps important to mention that at this stage, the templates the compile function gets are the source templates (not instance template).

DOM instances are often simply the result of a source template being rendered to the DOM, but they may be created by ng-repeat, or introduced on the fly.

Whenever a new instance of an element with a directive is rendered to the DOM, the link phase starts.

In this phase, Angular calls controller, pre-link, iterates children, and call post-link on all directives, like so:

@lzhaki The flowchart looks nice. Mind to share the charting tool's name? :)

@merlin I've used OmniGraffle (but could have used illustrator or inkscape - other than speed, there's nothing OmniGraffle does better than other charting tools as far as this illustration is concerned).

@Anant's plunker disappeared so here's a new one: plnkr.co/edit/kZZks8HN0iFIY8ZaKJkA?p=preview Open the JS console to see the log statements

WHY this is not true when ng-repeat is used for children directives??? See plunk:plnkr.co/edit/HcH4r6GV5jAFC3yOZknc?p=preview

@Luckylooke Your plunk has got no children with directive under ng-repeat (ie, what's being repeated is a template with a directive. If it would, you'd see that their compile is only called after the link of ng-repeat.

angularjs - Angular directives - when and how to use compile, controll...

angularjs angularjs-directive
Rectangle 27 161

In which order the directive functions are executed?

Based on the following plunk, consider the following HTML markup:

<body>
    <div log='some-div'></div>
</body>

With the following directive declaration:

myApp.directive('log', function() {

    return {
        controller: function( $scope, $element, $attrs, $transclude ) {
            console.log( $attrs.log + ' (controller)' );
        },
        compile: function compile( tElement, tAttributes ) {
            console.log( tAttributes.log + ' (compile)'  );
            return {
                pre: function preLink( scope, element, attributes ) {
                    console.log( attributes.log + ' (pre-link)'  );
                },
                post: function postLink( scope, element, attributes ) {
                    console.log( attributes.log + ' (post-link)'  );
                }
            };
         }
     };  

});
some-div (compile)
some-div (controller)
some-div (pre-link)
some-div (post-link)

We can see that compile is executed first, then controller, then pre-link and last is post-link.

Note: The following does not apply to directives that render their children in their link function. Quite a few Angular directives do so (like ngIf, ngRepeat, or any directive with transclude). These directives will natively have their link function called before their child directives compile is called.

The original HTML markup is often made of nested elements, each with its own directive. Like in the following markup (see plunk):

<body>
    <div log='parent'>
        <div log='..first-child'></div>
        <div log='..second-child'></div>
    </div>
</body>
// The compile phase
parent (compile)
..first-child (compile)
..second-child (compile)

// The link phase   
parent (controller)
parent (pre-link)
..first-child (controller)
..first-child (pre-link)
..first-child (post-link)
..second-child (controller)
..second-child (pre-link)
..second-child (post-link)
parent (post-link)

We can distinguish two phases here - the compile phase and the link phase.

When the DOM is loaded Angular starts the compile phase, where it traverses the markup top-down, and calls compile on all directives. Graphically, we could express it like so:

It is perhaps important to mention that at this stage, the templates the compile function gets are the source templates (not instance template).

DOM instances are often simply the result of a source template being rendered to the DOM, but they may be created by ng-repeat, or introduced on the fly.

Whenever a new instance of an element with a directive is rendered to the DOM, the link phase starts.

In this phase, Angular calls controller, pre-link, iterates children, and call post-link on all directives, like so:

@lzhaki The flowchart looks nice. Mind to share the charting tool's name? :)

@merlin I've used OmniGraffle (but could have used illustrator or inkscape - other than speed, there's nothing OmniGraffle does better than other charting tools as far as this illustration is concerned).

@Anant's plunker disappeared so here's a new one: plnkr.co/edit/kZZks8HN0iFIY8ZaKJkA?p=preview Open the JS console to see the log statements

WHY this is not true when ng-repeat is used for children directives??? See plunk:plnkr.co/edit/HcH4r6GV5jAFC3yOZknc?p=preview

@Luckylooke Your plunk has got no children with directive under ng-repeat (ie, what's being repeated is a template with a directive. If it would, you'd see that their compile is only called after the link of ng-repeat.

angularjs - Angular directives - when and how to use compile, controll...

angularjs angularjs-directive
Rectangle 27 164

This is an interesting question, and I started thinking about how I would implement something like this.

Basically, instead of trying to call a directive from a controller, I created a module to house all the popdown logic:

var PopdownModule = angular.module('Popdown', []);

I put two things in the module, a factory for the API which can be injected anywhere, and the directive for defining the behavior of the actual popdown element:

The factory just defines a couple of functions success and error and keeps track of a couple of variables:

PopdownModule.factory('PopdownAPI', function() {
    return {
        status: null,
        message: null,
        success: function(msg) {
            this.status = 'success';
            this.message = msg;
        },
        error: function(msg) {
            this.status = 'error';
            this.message = msg;
        },
        clear: function() {
            this.status = null;
            this.message = null;
        }
    }
});

The directive gets the API injected into its controller, and watches the api for changes (I'm using bootstrap css for convenience):

PopdownModule.directive('popdown', function() {
    return {
        restrict: 'E',
        scope: {},
        replace: true,
        controller: function($scope, PopdownAPI) {
            $scope.show = false;
            $scope.api = PopdownAPI;

            $scope.$watch('api.status', toggledisplay)
            $scope.$watch('api.message', toggledisplay)

            $scope.hide = function() {
                $scope.show = false;
                $scope.api.clear();
            };

            function toggledisplay() {
                $scope.show = !!($scope.api.status && $scope.api.message);               
            }
        },
        template: '<div class="alert alert-{{api.status}}" ng-show="show">' +
                  '  <button type="button" class="close" ng-click="hide()">&times;</button>' +
                  '  {{api.message}}' +
                  '</div>'
    }
})

Then I define an app module that depends on Popdown:

var app = angular.module('app', ['Popdown']);

app.controller('main', function($scope, PopdownAPI) {
    $scope.success = function(msg) { PopdownAPI.success(msg); }
    $scope.error   = function(msg) { PopdownAPI.error(msg); }
});
<html ng-app="app">
    <body ng-controller="main">
        <popdown></popdown>
        <a class="btn" ng-click="success('I am a success!')">Succeed</a>
        <a class="btn" ng-click="error('Alas, I am a failure!')">Fail</a>
    </body>
</html>

I'm not sure if it's completely ideal, but it seemed like a reasonable way to set up communication with a global-ish popdown directive.

+1 One should never call a function in a directive from outside the directive - it's bad practice. Using a service to manage global state that a directive reads is super common and this is the correct approach. More applications include notification queues and modal dialogs.

Really exceptional answer! Such a useful example for those of us coming from jQuery and Backbone

In this way is it possible to use this module to instantiate multiple directives in the same view? How can I call the success or error function of a particular instance of this directive?

@ira you could probably change the factory to keep a map (or list) of status and message objects and then use a name attribute on the directive to identify which item in the list you need. So instead of calling success(msg) in the html you would call sucess(name, msg) to select the directive with the correct name.

@JoshDavidMiller why do you consider it bad practice to call a method on a directive? If a directive encapsulates the some DOM logic as intended, surely it is quite natural to expose an API so that controllers that use it can invoke its methods as needed?

javascript - Call method in directive controller from other controller...

javascript jquery angularjs angularjs-directive
Rectangle 27 164

This is an interesting question, and I started thinking about how I would implement something like this.

Basically, instead of trying to call a directive from a controller, I created a module to house all the popdown logic:

var PopdownModule = angular.module('Popdown', []);

I put two things in the module, a factory for the API which can be injected anywhere, and the directive for defining the behavior of the actual popdown element:

The factory just defines a couple of functions success and error and keeps track of a couple of variables:

PopdownModule.factory('PopdownAPI', function() {
    return {
        status: null,
        message: null,
        success: function(msg) {
            this.status = 'success';
            this.message = msg;
        },
        error: function(msg) {
            this.status = 'error';
            this.message = msg;
        },
        clear: function() {
            this.status = null;
            this.message = null;
        }
    }
});

The directive gets the API injected into its controller, and watches the api for changes (I'm using bootstrap css for convenience):

PopdownModule.directive('popdown', function() {
    return {
        restrict: 'E',
        scope: {},
        replace: true,
        controller: function($scope, PopdownAPI) {
            $scope.show = false;
            $scope.api = PopdownAPI;

            $scope.$watch('api.status', toggledisplay)
            $scope.$watch('api.message', toggledisplay)

            $scope.hide = function() {
                $scope.show = false;
                $scope.api.clear();
            };

            function toggledisplay() {
                $scope.show = !!($scope.api.status && $scope.api.message);               
            }
        },
        template: '<div class="alert alert-{{api.status}}" ng-show="show">' +
                  '  <button type="button" class="close" ng-click="hide()">&times;</button>' +
                  '  {{api.message}}' +
                  '</div>'
    }
})

Then I define an app module that depends on Popdown:

var app = angular.module('app', ['Popdown']);

app.controller('main', function($scope, PopdownAPI) {
    $scope.success = function(msg) { PopdownAPI.success(msg); }
    $scope.error   = function(msg) { PopdownAPI.error(msg); }
});
<html ng-app="app">
    <body ng-controller="main">
        <popdown></popdown>
        <a class="btn" ng-click="success('I am a success!')">Succeed</a>
        <a class="btn" ng-click="error('Alas, I am a failure!')">Fail</a>
    </body>
</html>

I'm not sure if it's completely ideal, but it seemed like a reasonable way to set up communication with a global-ish popdown directive.

+1 One should never call a function in a directive from outside the directive - it's bad practice. Using a service to manage global state that a directive reads is super common and this is the correct approach. More applications include notification queues and modal dialogs.

Really exceptional answer! Such a useful example for those of us coming from jQuery and Backbone

In this way is it possible to use this module to instantiate multiple directives in the same view? How can I call the success or error function of a particular instance of this directive?

@ira you could probably change the factory to keep a map (or list) of status and message objects and then use a name attribute on the directive to identify which item in the list you need. So instead of calling success(msg) in the html you would call sucess(name, msg) to select the directive with the correct name.

@JoshDavidMiller why do you consider it bad practice to call a method on a directive? If a directive encapsulates the some DOM logic as intended, surely it is quite natural to expose an API so that controllers that use it can invoke its methods as needed?

Sign up for our newsletter and get our top new questions delivered to your inbox (see an example).

javascript - Call method in directive controller from other controller...

javascript jquery angularjs angularjs-directive
Rectangle 27 113

Since the directive is only calling a function (and not trying to set a value on a property), you can use $eval instead of $parse (with a non-isolated scope):

scope.$apply(function() {
    scope.$eval(attrs.confirmAction);
});

Or better, simply just use $apply, which will $eval()uate its argument against the scope:

scope.$apply(attrs.confirmAction);

Did not know that, thanks for that. I've always thought the $parse method was a bit too wordy.

@CMCDragonkai, if the function has arguments, you could specify them in the HTML, confirm-action="doIt(arg1)", and then set scope.arg1 before calling $eval. However, it would probably be cleaner to use $parse: stackoverflow.com/a/16200618/215945

Is it not possible to do scope.$eval(attrs.doIt({arg1: 'blah'});?

@CMCDragonkai, no, scope.$eval(attrs.confirmAction({arg1: 'blah'}) won't work. You would need to use $parse with that syntax.

Call a controller function from a directive without isolated scope in ...

angularjs angularjs-directive angularjs-scope
Rectangle 27 336

If you want to use isolated scopes you can pass a control object using bi-directional binding = of a variable from the controller scope. You can also control also several instances of the same directive on a page with the same control object.

angular.module('directiveControlDemo', [])

.controller('MainCtrl', function($scope) {
  $scope.focusinControl = {};
})

.directive('focusin', function factory() {
  return {
    restrict: 'E',
    replace: true,
    template: '<div>A:{{internalControl}}</div>',
    scope: {
      control: '='
    },
    link: function(scope, element, attrs) {
      scope.internalControl = scope.control || {};
      scope.internalControl.takenTablets = 0;
      scope.internalControl.takeTablet = function() {
        scope.internalControl.takenTablets += 1;
      }
    }
  };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="directiveControlDemo">
  <div ng-controller="MainCtrl">
    <button ng-click="focusinControl.takeTablet()">Call directive function</button>
    <p>
      <b>In controller scope:</b>
      {{focusinControl}}
    </p>
    <p>
      <b>In directive scope:</b>
      <focusin control="focusinControl"></focusin>
    </p>
    <p>
      <b>Without control object:</b>
      <focusin></focusin>
    </p>
  </div>
</div>

This is cleaner than the accepted answer, and +1 for the simpsons reference, if I'm not mistaken

That's exatcly how I solved the same problem. It works, but it looks like a hack... I wish angular had a better solution for this.

I'm learning angular, so my opinion may not hold much weight, but I found this approach much more intuitive than the other answer and would have marked it the correct answer. I implemented this in my sandbox application with zero trouble.

You should probably do a check to make sure scope.control exists, otherwise other places that use the directive but don't need to access the directive's methods and don't have a control attr will start throwing errors about not being able to set attributes on undefined

How to call a method defined in an AngularJS directive? - Stack Overfl...

angularjs angularjs-directive
Rectangle 27 335

If you want to use isolated scopes you can pass a control object using bi-directional binding = of a variable from the controller scope. You can also control also several instances of the same directive on a page with the same control object.

angular.module('directiveControlDemo', [])

.controller('MainCtrl', function($scope) {
  $scope.focusinControl = {};
})

.directive('focusin', function factory() {
  return {
    restrict: 'E',
    replace: true,
    template: '<div>A:{{internalControl}}</div>',
    scope: {
      control: '='
    },
    link: function(scope, element, attrs) {
      scope.internalControl = scope.control || {};
      scope.internalControl.takenTablets = 0;
      scope.internalControl.takeTablet = function() {
        scope.internalControl.takenTablets += 1;
      }
    }
  };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="directiveControlDemo">
  <div ng-controller="MainCtrl">
    <button ng-click="focusinControl.takeTablet()">Call directive function</button>
    <p>
      <b>In controller scope:</b>
      {{focusinControl}}
    </p>
    <p>
      <b>In directive scope:</b>
      <focusin control="focusinControl"></focusin>
    </p>
    <p>
      <b>Without control object:</b>
      <focusin></focusin>
    </p>
  </div>
</div>

This is cleaner than the accepted answer, and +1 for the simpsons reference, if I'm not mistaken

That's exatcly how I solved the same problem. It works, but it looks like a hack... I wish angular had a better solution for this.

I'm learning angular, so my opinion may not hold much weight, but I found this approach much more intuitive than the other answer and would have marked it the correct answer. I implemented this in my sandbox application with zero trouble.

You should probably do a check to make sure scope.control exists, otherwise other places that use the directive but don't need to access the directive's methods and don't have a control attr will start throwing errors about not being able to set attributes on undefined

How to call a method defined in an AngularJS directive? - Stack Overfl...

angularjs angularjs-directive
Rectangle 27 333

If you want to use isolated scopes you can pass a control object using bi-directional binding = of a variable from the controller scope. You can also control also several instances of the same directive on a page with the same control object.

angular.module('directiveControlDemo', [])

.controller('MainCtrl', function($scope) {
  $scope.focusinControl = {};
})

.directive('focusin', function factory() {
  return {
    restrict: 'E',
    replace: true,
    template: '<div>A:{{internalControl}}</div>',
    scope: {
      control: '='
    },
    link: function(scope, element, attrs) {
      scope.internalControl = scope.control || {};
      scope.internalControl.takenTablets = 0;
      scope.internalControl.takeTablet = function() {
        scope.internalControl.takenTablets += 1;
      }
    }
  };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="directiveControlDemo">
  <div ng-controller="MainCtrl">
    <button ng-click="focusinControl.takeTablet()">Call directive function</button>
    <p>
      <b>In controller scope:</b>
      {{focusinControl}}
    </p>
    <p>
      <b>In directive scope:</b>
      <focusin control="focusinControl"></focusin>
    </p>
    <p>
      <b>Without control object:</b>
      <focusin></focusin>
    </p>
  </div>
</div>

This is cleaner than the accepted answer, and +1 for the simpsons reference, if I'm not mistaken

That's exatcly how I solved the same problem. It works, but it looks like a hack... I wish angular had a better solution for this.

I'm learning angular, so my opinion may not hold much weight, but I found this approach much more intuitive than the other answer and would have marked it the correct answer. I implemented this in my sandbox application with zero trouble.

You should probably do a check to make sure scope.control exists, otherwise other places that use the directive but don't need to access the directive's methods and don't have a control attr will start throwing errors about not being able to set attributes on undefined

How to call a method defined in an AngularJS directive? - Stack Overfl...

angularjs angularjs-directive
Rectangle 27 1

<my-custom-directive my-callback="test.callbackMethod($index)" ng-repeat="item in test.items track by $index" theindex="$index"></my-custom-directive>

angularjs - Unable to call parent controller method from directive - S...

angularjs angularjs-directive angularjs-controller
Rectangle 27 3

here is my directive , I have injected on object reference in directive and has extend that by adding invoke function in directive code .

app.directive('myDirective', function () {
    return {
        restrict: 'E',
        scope: {
        /*The object that passed from the cntroller*/
        objectToInject: '=',
        },
        templateUrl: 'templates/myTemplate.html',

        link: function ($scope, element, attrs) {
            /*This method will be called whet the 'objectToInject' value is changes*/
            $scope.$watch('objectToInject', function (value) {
                /*Checking if the given value is not undefined*/
                if(value){
                $scope.Obj = value;
                    /*Injecting the Method*/
                    $scope.Obj.invoke = function(){
                        //Do something
                    }
                }    
            });
        }
    };
});

Declaring the directive in the HTML with a parameter:

<my-directive object-to-inject="injectedObject"></ my-directive>
app.controller("myController", ['$scope', function ($scope) {
   // object must be empty initialize,so it can be appended
    $scope.injectedObject = {};

    // now i can directly calling invoke function from here 
     $scope.injectedObject.invoke();
}];

This basically goes against the separation of concerns principles. You provide to the directive an object instantiated in a controller, and you delegate the responsibility of managing that object (ie creation of the invoke function) to the directive. In my opinion, NOT the better solution.

javascript - Call method in directive controller from other controller...

javascript jquery angularjs angularjs-directive
Rectangle 27 3

here is my directive , I have injected on object reference in directive and has extend that by adding invoke function in directive code .

app.directive('myDirective', function () {
    return {
        restrict: 'E',
        scope: {
        /*The object that passed from the cntroller*/
        objectToInject: '=',
        },
        templateUrl: 'templates/myTemplate.html',

        link: function ($scope, element, attrs) {
            /*This method will be called whet the 'objectToInject' value is changes*/
            $scope.$watch('objectToInject', function (value) {
                /*Checking if the given value is not undefined*/
                if(value){
                $scope.Obj = value;
                    /*Injecting the Method*/
                    $scope.Obj.invoke = function(){
                        //Do something
                    }
                }    
            });
        }
    };
});

Declaring the directive in the HTML with a parameter:

<my-directive object-to-inject="injectedObject"></ my-directive>
app.controller("myController", ['$scope', function ($scope) {
   // object must be empty initialize,so it can be appended
    $scope.injectedObject = {};

    // now i can directly calling invoke function from here 
     $scope.injectedObject.invoke();
}];

This basically goes against the separation of concerns principles. You provide to the directive an object instantiated in a controller, and you delegate the responsibility of managing that object (ie creation of the invoke function) to the directive. In my opinion, NOT the better solution.

javascript - Call method in directive controller from other controller...

javascript jquery angularjs angularjs-directive
Rectangle 27 26

You can also use events to trigger the Popdown.

Here's a fiddle based on satchmorun's solution. It dispenses with the PopdownAPI, and the top-level controller instead $broadcasts 'success' and 'error' events down the scope chain:

$scope.success = function(msg) { $scope.$broadcast('success', msg); };
$scope.error   = function(msg) { $scope.$broadcast('error', msg); };
$scope.$on('success', function(event, msg) {
    $scope.status = 'success';
    $scope.message = msg;
    $scope.toggleDisplay();
});

This works, at least, and seems to me to be a nicely decoupled solution. I'll let others chime in if this is considered poor practice for some reason.

One drawback that I can think of is that in the selected answer you only need the PopdownAPI (easily available with DI). In this one you need access to the controller's scope to broadcast the message. Anyway, it looks very concise.

I like this better than the service-approach for simple use cases as it keeps the complexity down and is still loosely coupled

javascript - Call method in directive controller from other controller...

javascript jquery angularjs angularjs-directive
Rectangle 27 26

You can also use events to trigger the Popdown.

Here's a fiddle based on satchmorun's solution. It dispenses with the PopdownAPI, and the top-level controller instead $broadcasts 'success' and 'error' events down the scope chain:

$scope.success = function(msg) { $scope.$broadcast('success', msg); };
$scope.error   = function(msg) { $scope.$broadcast('error', msg); };
$scope.$on('success', function(event, msg) {
    $scope.status = 'success';
    $scope.message = msg;
    $scope.toggleDisplay();
});

This works, at least, and seems to me to be a nicely decoupled solution. I'll let others chime in if this is considered poor practice for some reason.

One drawback that I can think of is that in the selected answer you only need the PopdownAPI (easily available with DI). In this one you need access to the controller's scope to broadcast the message. Anyway, it looks very concise.

I like this better than the service-approach for simple use cases as it keeps the complexity down and is still loosely coupled

javascript - Call method in directive controller from other controller...

javascript jquery angularjs angularjs-directive
Rectangle 27 12

There are two ways, with which we can call using & and =.

If I am using = for a scope attribute

ng-click='updateparent({person: mandatePerson})'
ng-click='updateparent(mandatePerson)'

And in the directive,

updateparent="updatePerson()"
updateparent="updatePerson"

No need to mention arguments here, they will be passed to controller's function definition as a reference.

Thank you, passing the controller's method using '=' is easier to work with than using '&' because I don't have to declare all the arguments in the directive instance

I've just spent an entire afternoon trying to fix a directive. The problem was the arguments in directive declaration (because they existed the argument passed was undefined). Thank you so much!

calling method of parent controller from a directive in AngularJS - St...

angularjs angularjs-directive
Rectangle 27 65

Assuming that the action button uses the same controller $scope as the directive, just define function updateMap on $scope inside the link function. Your controller can then call that function when the action button is clicked.

<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>',
        link: function($scope, element, attrs) {
            $scope.updateMap = function() {
                alert('inside updateMap()');
            }
        }
    }
});

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 the map directive which will register the directive function with the controller:

<map set-fn="setDirectiveFn(theDirFn)"></map>
<button ng-click="directiveFn()">call directive function</button>
scope: { setFn: '&' },
link: function(scope, element, attrs) {
    scope.updateMap = function() {
       alert('inside updateMap()');
    }
    scope.setFn({theDirFn: scope.updateMap});
}
function MyCtrl($scope) {
    $scope.setDirectiveFn = function(directiveFn) {
        $scope.directiveFn = directiveFn;
    };
}

Thanks! (Maybe it would be easier to call a function defined in the directive's controller but I'm not sure about that)

This is much better way if you are not dealing with an isolated scope.

This answer does actually answers the OP question. It does also use isolated scope, in order to have an isolated scope you only need to add the scope property into the directive declaration.

How to call a method defined in an AngularJS directive? - Stack Overfl...

angularjs angularjs-directive