0

I have a controller like this:

angular.module('main', []).controller('mainCtrl', function ($scope) { $scope.doStuff = function (cb) { // Do some stuff. cb(); }; }); 

And I have a directive like this:

angular.module('action-bar', []).directive('actionBar', function () { return { restrict: 'EA', replace: true, template: '<h1>test</h1>', scope: { doStuff: '&' }, link: function (scope, element, attrs) { scope.doStuff(function () { alert('callback executed'); }); } }; }); 

Here is the markup:

<div ng-app="main"> <div ng-controller="mainCtrl"> <div ng-action-bar></div> </div> </div> 

I can call doStuff just fine, however the cb argument is always undefined even though I'm passing in an anonymous function as the callback. What am I doing wrong here?

4
  • Did you actually pass the doStuff attribute to your actionBar directive in your HTML? You didn't give us a sample of how you are using it. Just from a quick glance I think you need to add a doStuff="doStuff()" to your HTML.. or just create a jsfiddle so we can see it better Commented Mar 31, 2014 at 18:10
  • possible duplicate of How to pass argument to method defined in controller but called from directive in Angularjs? Commented Mar 31, 2014 at 18:38
  • @JoseM Sorry, I left out the markup. The directive is contained within the element that is controlled by mainCtrl. Therefore the doStuff function is inherited into the isolate scope from the controller scope. Commented Mar 31, 2014 at 19:18
  • @Stewie this is not a duplicate because I am not trying to pass anything in through attrs. I'm simply trying to pass an argument to the parent scope method from within the link function. Commented Mar 31, 2014 at 19:25

3 Answers 3

2

Changing your isolate scope to use = instead of & allows you to access the parent scopes doStuff method.

angular.module('main', ['action-bar']).controller('mainCtrl', function ($scope) { $scope.doStuff = function (cb) { // Do some stuff. cb(); }; }); angular.module('action-bar', []).directive('actionBar', function () { return { restrict: 'EA', replace: true, template: '<h1>test</h1>', scope: { doStuff: '=' }, link: function (scope, element, attrs) { scope.doStuff(function () { alert('callback executed'); }); } }; }); 

Using the directive in you markup

<div ng-app="main" ng-controller="mainCtrl"> <action-bar do-stuff="doStuff"> </action-bar> </div> 

Here is a working example.

Sign up to request clarification or add additional context in comments.

5 Comments

So, it looks like if you don't include the do-stuff="doStuff" attribute then it doesn't work. Is there a way to make this possible without specifying any attributes in the markup?
Sure if you remove the isolate scope e.g(scope: { doStuff: '=' },) it should work without having to pass in the method as a parameter. Fiddle
I guess I can't do that because my team has their reasons for having the isolate scope. I think my big failure here was that I didn't know about scope.$parent, which I just discovered. Calling scope.$parent.doStuff(function () { ... }) seems to work just as expected :)
That works as well, Although using $parent can cause headaches in certain cases.
I can see that, but in this case I literally am trying to call a simple parent function from inside an isolated scope. I don't actually need the parent function to be in the isolated scope at all. This seems like the "proper" solution, if there even is one.
0

If you want to use the expression ('&') syntax for passing doStuff into your directive then in your html you should pass it in like this:

<div action-bar doStuff="doStuff(cb)"></div> 

and in your directive's link function you can call it like this:

scope.doStuff({cb: function() { ... }}); 

Checkout the egghead.io video on it for more info: https://egghead.io/lessons/angularjs-isolate-scope-expression-binding

Note: I personally don't like this syntax so I always use Jonathan Palumbo's solution (two way binding).

Comments

0

I solved this by simply using scope.$parent to access the parent scope. I don't actually need the parent function to be part of the directive's isolate scope.

Example:

var app = angular.module('app', []); // Main controller. app.controller('mainCtrl', function ($scope) { // Initial text added to log. $scope.logs = ['App loaded.']; // A log function to add additional lines to the log. $scope.log = function (text, callback) { $scope.logs.push(text); if (callback) callback(); }; }); // Directive to log lines to the log viewer. app.directive('ngLogLine', function () { return { restrict: 'E', template: '<div><input type="text" ng-model="msg" ng-keydown="$event.keyCode === 13 ? log() : null" /><button ng-click="log()">send</button></div>', replace: true, scope: {}, link: function (scope, element, attrs) { // Create our own log function on our isolate scope. scope.log = function () { // Call parent log function and pass in a callback. scope.$parent.log(scope.msg, function () { // Clear the msg, clearing the input. scope.msg = ''; }); }; } }; }); 

Codepen: http://codepen.io/Chevex/pen/eHuaK/

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.