2

I have a directive called <popup> that contains a number of <popup-link>-directives. When a <popup-link> is clicked, the parent <popup> directive should be closed by invoking a function close() on its controller.

For some reason I cannot get the controller instance of <popup>, as it is not injected correctly to the link function of <popup-link>

I get the below error and the inspecting the object yields instantiate.c ?

Object [object Object] has no method 'close'

What am I doing wrong?

directive('popup', function () {
    return {
        restrict: 'EA',
        replace: true,
        transclude: true,
        template: '<div id="{{ popupId }}" class="navigatorPopup" ng-transclude></div>',
        controller: function ($scope) {
            $scope.close = function () {
                //close popup
            };
        },
        link: function (scope, element, attr) {
            //
        }
    }
}).
directive('popupLink', function () {
    return {
        restrict: 'EA',
        require: '^popup',
        template: '<h3 ng-bind="title"></h3>',
        replace: true,
        scope: {
            title: '@',
            ngClick: '&'
        },
        link: function (scope, element, attr, popupCtrl) {
            scope.popupCtrl = popupCtrl;
            element.bind('click', 
                function () {
                    scope.popupCtrl.close();
                    scope.ngClick();
                }
            );
        }
    }
});

And the HTML

<popup name="menuNavigator">
   <popup-link ng-repeat="category in getCategories()" title="{{ category.Title }}" ng-click="navigateMenu($index)"></popup-link>
</popup>

Thanks!

Jeppebm
  • 389
  • 1
  • 3
  • 16
  • 2
    For an explanation of why you need to define `close` on the controller rather than the `$scope`, see http://stackoverflow.com/questions/11605917/this-vs-scope-in-angularjs-controllers – Mark Rajcok Jul 14 '13 at 02:36

2 Answers2

2

The child controller is requiring an instance of the parent controller. Treat the "public" components of the parent controller as, well, public.

Change $scope.close = function(){..} to this.close = function(){..}

Also, it is not necessary to explicitly set popupCtrl to the local scope as you did in your child link function. You can access the parent function directly with:

popupCtrl.close()

Unless you just prefer that naming convention...

DEMO

rGil
  • 3,719
  • 1
  • 22
  • 30
1

You're not wrapping an scope.$apply around methods within the click handler:

element.bind('click', 
    function () {
        scope.$apply(function(){
            scope.popupCtrl.close();
            scope.ngClick();
        });
    }
);

You need to wrap any code that happens outside of AngularJS' $apply/$digest loop with a scope.$apply method so that Angularjs is aware that the change has happened.

Editted:

Sorry i misread question.

The reason this is happening is you're putting the function on the scope property instead of the controller:

controller: function ($scope) {
    $scope.close = function () {
        //close popup
    };
},

should be:

controller: function ($scope) {
    this.close = function () {
        //close popup
    };
},
Clark Pan
  • 6,027
  • 1
  • 22
  • 18
  • This is true for instances such as scope variables being modified within the handler. But in this case, if you are just calling other functions, they will fire without the $apply. – rGil Jul 14 '13 at 00:19