Patrick McDermott
Patrick McDermott

Reputation: 1220

Unable to access elements parent controller

I am following a tutorial to implement a drop down menu with AngularJS's sidenav. I have used components in my application so my layout differs from the example provided.

The ul is populated with the names stored in an object, but the functions are failing. I am narrowed it down to work out the error is due to not being able to find the directives parent elements controller.

var controller = $element.parent().controller();

When I log controller in the console, it should display the controllers functions which are:

vm.isOpen = isOpen;
vm.toggleOpen = toggleOpen;
vm.autoFocusContent = false;
vm.menu = mainNavService;
vm.status = {
    isFirstOpen: true,
    isFirstDisabled: false
};

But instead it returns and empty object. Is it because rather than use a controller, I am using Angular's component method on the module and using the controller property?

Question

Why when trying to access the controller, it returns no properties?

app.component('mainnav', {
    templateUrl: 'p3sweb/app/components/app/views/main-nav.htm',
    controller: ['userService', 'mainNavService', function(userService, mainNavService){

        var vm = this;

        vm.isOpen = isOpen;
        vm.toggleOpen = toggleOpen;
        vm.autoFocusContent = false;
        vm.menu = mainNavService;
        vm.status = {
          isFirstOpen: true,
          isFirstDisabled: false
        };
        function isOpen(section) {
            console.log('menu.isSectionSelected(section)')
          return menu.isSectionSelected(section);
        }
        function toggleOpen(section) {
                console.log(menu.toggleSelectSection(section))
          menu.toggleSelectSection(section);
        }
    }]
})

app.directive('menuToggle', [ '$timeout', function($timeout){
    return {
        scope: {
            section: '='
        },
        templateUrl: 'p3sweb/app/components/app/views/main-nav-li.htm',
        link: function($scope, $element) {

            var controller = $element.parent().controller(); //FAILS

            $scope.isOpen = function() {
                return controller.isOpen($scope.section)
            };
            $scope.toggle = function() {
                console.log(controller.toggleOpen())
                controller.toggleOpen($scope.section);
            };
        }
    };
}])

Upvotes: 0

Views: 67

Answers (2)

AdityaParab
AdityaParab

Reputation: 7100

A javascript context can be passed around pretty easily. What you can do is, in change your menuToggle directive like

app.directive('menuToggle', [ '$timeout', function($timeout){
    return {
        scope: {
            section: '=',
            context: '=' // NOTE: This is what the parent component will pass
        },
        templateUrl: 'p3sweb/app/components/app/views/main-nav-li.htm',
        link: function($scope, $element) {
            var controller = $scope.context; // This is reference to parent
            $scope.isOpen = function() {
                return controller.isOpen($scope.section)
            };
            $scope.toggle = function() {
                console.log(controller.toggleOpen())
                controller.toggleOpen($scope.section);
            };
        }
    };
}])

And Inside your main-nav.htm you need to do

<menu-toggle section="blah" context="$ctrl"></menu-toggle>

EDIT::

The functions $scope.isOpen and $scope.toggle are also unnecessary. (unless you have some specific requirement to have them the way you have now)

In your main-nav-li.htm Wherever you find isOpen() replace that with context.isOpen(section) and wherever you have toggle(), Replace that with context.toggle(section)

Upvotes: 1

Kenji Mukai
Kenji Mukai

Reputation: 599

I ran your code in jsfiddle and this is what I changed:

First I changed the component to a directive adding the transclude option:

app.directive('mainnav', function(){
    return {
      transclude: true,
      template: '<div> <ng-transclude></ng-transclude> <div>',
      controller: ['$scope', function($scope){
          var vm = this;
          vm.autoFocusContent = false;
          vm.status = {
            isFirstOpen: true,
            isFirstDisabled: false
          };

      }]
    }
});

After this you can access the parent controller like this:

var controller = $element.parent().controller('mainnav');

Here is the full example:

https://jsfiddle.net/jm65ajjz/2/

Upvotes: 0

Related Questions