angularjsangularjs-directiveng-showangularjs-ng-transclude

Toggle with ng-show not working in directive


I have directive that brings up the sidebar in my application. I tried to write some code to toggle some content inside this sidebar. But when I trigger the toggle function nothing happens.

This is my directive:

menuItem.directive("menuItem", function() {
 return {
     restrict: "E",
     template: "<div ng-click='toggle()' ng-transclude></div>",
     transclude: true,
     scope: {
         hash: "@"
     },
     link: function($scope) {
         $scope.toggle = function(e) {
             $scope.test = !$scope.test;
             console.log($scope.test);
             $scope.$apply(); // This here brings up error $rootScope: inprog
         }
     }
 }
});

And here is my html:

 <menu visible="leftVisible" alignment="left">
                          <menu-item hash="first-page">
                              <div>
                                  Testing
                              </div>
                              <ul ng-show="test">
                                 <li>1</li>
                                 <li>2</li>
                              </ul>
                          </menu-item>
                </menu>

How should I resolve this problem, to make the ul toggleable?


Solution

  • The reason is that your directive and your template have different scopes. When you change test field inside toggle method, you change it in directive scope, whereas ng-show directive uses variable from controller scope. And your directive should not depend on controller's markup (and controller-specific variables, like test). You can create a variable binding in your directive so that it will be responsible for changing visibility and controller markup will be responsible for using this variable to show or hide your menu items. Here is a markup for your controller

    <menu visible="leftVisible" alignment="left">
        <menu-item hash="first-page" show="someVar">
            <div>
                Testing
            </div>
            <ul ng-show="someVar">
                <li>1</li>
                <li>2</li>
            </ul>
        </menu-item>
    </menu>
    

    And here is a code for your directive

    menuItem.directive("menuItem", function () {
        return {
            restrict: "E",
            template: "<div ng-click='toggle()' ng-transclude></div>",
            transclude: true,
            scope: {
                hash: "@",
                show: '='
            },
            link: function ($scope) {
                $scope.toggle = function (e) {
                    $scope.show = !$scope.show;
                }
            }
        }
    });
    

    In this case your directive doesn't depend on myVar variable. It controls visibility using show attribute. I've also removed $apply call since your handler called using ng-click directive which means that angular is aware of possible changes and you don't have to explicitly start digest cycle.