0

I have to display a sub-menu when clicking on a link within the parent dropdown menu, when displaying, the parent should hide and the child show instead

what i have tried :

 <ul class="nav navbar-nav navbar-right " >
        <li class="dropdown">
            <a class="dropdown-toggle" data-toggle="dropdown" title=""></a>
               <ul id="dropdown-menu" class="dropdown-menu" role="menu" ng-hide="showSubMenu">
                  <li> <a href="#"  ng-click="showSubMenu = ! showSubMenu">Hide me and show submenu</a></li>
               </ul>
               <ul  class="dropdown-menu" role="menu"  ng-hide="!showSubMenu">
                  <li><a href="#" ng-click="showSubMenu =!showSubMenu">Hide me and t show back parent</a></li> 
              </ul>
         </li>
   </ul>                                       

changing visibility is working, but the drop event is not triggered, i have to click again to toggle the dropdown, i need that to be done automatically after changing the visibility state

1 Answer 1

1

You have to custom the drop down with Angular sintax

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

app.directive('offClick', ['$rootScope', '$parse', function ($rootScope, $parse) {
    var id = 0;
    var listeners = {};
    // add variable to detect touch users moving..
    var touchMove = false;

    // Add event listeners to handle various events. Destop will ignore touch events
    document.addEventListener("touchmove", offClickEventHandler, true);
    document.addEventListener("touchend", offClickEventHandler, true);
    document.addEventListener('click', offClickEventHandler, true);

    function targetInFilter(target, elms) {
        if (!target || !elms) return false;
        var elmsLen = elms.length;
        for (var i = 0; i < elmsLen; ++i) {
            var currentElem = elms[i];
            var containsTarget = false;
            try {
                containsTarget = currentElem.contains(target);
            } catch (e) {
                // If the node is not an Element (e.g., an SVGElement) node.contains() throws Exception in IE,
                // see https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect
                // In this case we use compareDocumentPosition() instead.
                if (typeof currentElem.compareDocumentPosition !== 'undefined') {
                    containsTarget = currentElem === target || Boolean(currentElem.compareDocumentPosition(target) & 16);
                }
            }

            if (containsTarget) {
                return true;
            }
        }
        return false;
    }

    function offClickEventHandler(event) {
        // If event is a touchmove adjust touchMove state
        if (event.type === 'touchmove') {
            touchMove = true;
            // And end function
            return false;
        }
        // This will always fire on the touchend after the touchmove runs...
        if (touchMove) {
            // Reset touchmove to false
            touchMove = false;
            // And end function
            return false;
        }
        var target = event.target || event.srcElement;
        angular.forEach(listeners, function (listener, i) {
            if (!(listener.elm.contains(target) || targetInFilter(target, listener.offClickFilter))) {
                $rootScope.$evalAsync(function () {
                    listener.cb(listener.scope, {
                        $event: event
                    });
                })
            }

        });
    }

    return {
        restrict: 'A',
        compile: function ($element, attr) {
            var fn = $parse(attr.offClick);
            return function (scope, element) {
                var elmId = id++;
                var offClickFilter;
                var removeWatcher;

                offClickFilter = document.querySelectorAll(scope.$eval(attr.offClickFilter));

                if (attr.offClickIf) {
                    removeWatcher = $rootScope.$watch(function () {
                        return $parse(attr.offClickIf)(scope);
                    }, function (newVal) {
                        if (newVal) {
                            on();
                        } else if (!newVal) {
                            off();
                        }
                    });
                } else {
                    on();
                }

                attr.$observe('offClickFilter', function (value) {
                    offClickFilter = document.querySelectorAll(scope.$eval(value));
                });

                scope.$on('$destroy', function () {
                    off();
                    if (removeWatcher) {
                        removeWatcher();
                    }
                });

                function on() {
                    listeners[elmId] = {
                        elm: element[0],
                        cb: fn,
                        scope: scope,
                        offClickFilter: offClickFilter
                    };
                }

                function off() {
                    delete listeners[elmId];
                }
            };
        }
    };
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>

<div ng-app="app">

<div class="dropdown" ng-class="{'show': dropdown}" off-click="dropdown=false;secDropdown=false">
  <button class="btn btn-secondary" type="button" ng-click="dropdown = !dropdown" >
    Dropdown button
  </button>
  <div class="dropdown-menu" ng-class="{'show': dropdown && !secDropdown}">
    <a class="dropdown-item" href="#" ng-click="secDropdown = true">Call Hero!</a>
  </div>
  <div class="dropdown-menu" ng-class="{'show': secDropdown}">
    <a class="dropdown-item" href="#">I'm Hero!</a>
    <div class="dropdown-divider"></div>
    <a class="dropdown-item" href="#" ng-click="secDropdown = false">Back</a>
  </div>
</div>

</div>

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

2 Comments

How to close the dropdown when clicking outside, in your example it's not working
you can use off-click directive it's already exist on the githup off-click

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.