AngularJS directive toggle menu preventing default for other directive

本小妞迷上赌 提交于 2020-01-13 06:02:12

问题


So I made a directive for a toggle (drop down) menu in AngularJS. I used the directive for multiple items within the page but I have a small problem. When one item is open and I click another one I want the previous one to close. The event.preventDefault and event.stopPropagation stops the event for the previous item and doesn't close it. Any ideas on how to fix this? Is there a way to perhaps only stop the event within the scope?

app.directive('toggleMenu', function ($document) {
    return {
        restrict: 'CA',
        link: function (scope, element, attrs) {
            var opened = false;
            var button = (attrs.menuButton ? angular.element(document.getElementById(attrs.menuButton)) : element.parent());
            var closeButton = (attrs.closeButton ? angular.element(document.getElementById(attrs.closeButton)) : false);

            var toggleMenu = function(){
                (opened ? element.fadeOut('fast') : element.fadeIn('fast'));
            };

            button.bind('click', function(event){
                event.preventDefault();
                event.stopPropagation();
                toggleMenu();
                opened = ! opened;
            });

            element.bind('click', function(event){
                if(attrs.stayOpen && event.target != closeButton[0]){
                    event.preventDefault();
                    event.stopPropagation();
                }
            });

            $document.bind('click', function(){
                if(opened){
                    toggleMenu();
                    opened = false;
                }
            });


        }
    };

And here's a Fiddle: http://jsfiddle.net/JknUJ/5/ Button opens content and content should close when clicked outside the div. When clicked on button 2 however content 1 doesn't close.


回答1:


Basic idea is that you need to share the state between all your dropdown submenus, so when one of them is shown, all others are hidden. The simpliest way of storing state (such as opened or closed) are... CSS classes!

We'll create a pair of directives - one for menu, and another for sumbenu. It is more expressive that just divs.

Here is out markup.

<menu>
  <submenu data-caption="Button 1">
    Content 1
  </submenu>
  <submenu data-caption="Button 2">
    Content 2
  </submenu>
</menu>

Look how readable is it! Say thanks to directives:

plunker.directive("menu", function(){
    return {
        restrict : "E",
        scope : {},
        transclude : true,
        replace : true,
        template : "<div class='menu' data-ng-transclude></div>",
        controller : function ($scope, $element, $attrs, $transclude){
            $scope.submenus = [];

            this.addSubmenu = function (submenu) {
                $scope.submenus.push(submenu);
            }

            this.closeAllSubmenus = function (doNotTouch){
                angular.forEach($scope.submenus, function(submenu){
                    if(submenu != doNotTouch){
                        submenu.close();    
                    }
                })
            }
        }
    }
});

plunker.directive("submenu", function(){
    return {
        restrict : "E",
        require : "^menu",
        scope : {
            caption : "@"
        },
        transclude : true,
        replace : true,
        template : "<div class='submenu'><label>{{caption}}</label><div class='submenu-content' data-ng-transclude></div></div>",
        link : function ($scope, $iElement, $iAttrs, menuController) {
            menuController.addSubmenu($scope);

            $iElement.bind("click", function(event){
                menuController.closeAllSubmenus($scope);
                $iElement.toggleClass("active");
            });

            $scope.close = function (){
                $iElement.removeClass("active");
            }
        }
    }
});

Look thar we restricted them to HTML elements (restrict : "E"). submenu requires to be nested in menu (require : "^menu"), this allows us to inject menu controller to submenu's link function. transclude and replace controls the position of original markup in compiled HTML output (replace=true means that original markup will be replaced with compiled, transclude inserts parts of original markup to compiled output).

When we've done with this, we just say to menu close all your child menus! and menu iterates over submenus, forcing them to close.

We are adding childs to menu controller in addSubmenu function. It is called in submenus link function, thus every compiled instance of submenu adds itself to menu. Now, closing all submenus is as easy as iterating over all children, this is done by closeAllSubmenus in menu controller.

Here is a full Plunker to play with.



来源:https://stackoverflow.com/questions/18171901/angularjs-directive-toggle-menu-preventing-default-for-other-directive

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!