Material: md-menu closing on every click even with $event.stopPropagation() being called

Created on 26 Aug 2015  路  18Comments  路  Source: angular/material

A little background: I needed a month picker so I started with md-menu as seen in this code pen
http://codepen.io/anon/pen/MwBMvW?editors=101

The issue I ran into was the menu closing on every click, I wanted the menu to stay open when the year is changed.

What would others think of adding an attribute along the lines of "md-menu-disable-close" to prevent a click from closing a menu?

Changing line 286 in https://github.com/angular/material/blob/master/src/components/menu/js/menuServiceProvider.js would allow for this.

if (!target.hasAttribute('disabled') && !target.hasAttribute('md-menu-disable-close') &&(!closestMenu || closestMenu == opts.parent[0])) { close(); }

Interested to hear others thoughts or if someone else has another solution.

Thanks

inconvenient

Most helpful comment

Solved this using ngMouseup instead of ngClick. No stopPropagation() required.

All 18 comments

If you need a quick workaround, don't use ng-click and register an onclick event instead and wrap it in $scope.$apply. Right now, coincidentally, I'm battling with the same issue.

Thanks clshortfuse, I was able to implement the method you suggested but it required me to use a setTimeout to wait for the menu to be built. Is there an event i could trigger this off of? I feel as if this may end up being something lost of us hit, is there a case for not having an attribute that disables the close?

    $mdOpenMenu(ev);
    setTimeout(function () {
        $("#preYear").bind('click', function (e) {
            scope.$apply(function () {
                scope.year = scope.year - 1 ;
                scope.setSelectedMonth();
            });
            e.stopPropagation()
        });
        $("#nextYear").bind('click', function (e) {
            scope.$apply(function () {
                scope.year++;
                scope.setSelectedMonth();
            });
            e.stopPropagation()
        });
    },500);

+1

@ratscrew here's what i did:

http://codepen.io/team/AngularMaterial/pen/BNYppz

It's similar to yours, but I used $timeout instead. I had another issue because I NEEDED ng-click to be created because I have a button within a button that gets triggered so I had to clear it. Since $timeout is part of angular, I don't think there will be a timing issue.

Ideally speaking I would go through every child element and take the ng-click function and "move" it to onClick, but I couldn't figure out how to grab the ng-click function over javascript.

Edit: I didn't try with master and it's failing. Let me see what I can come up with

@ratscrew

New post because my old one wasn't using master:

http://codepen.io/anon/pen/LpPjBr

You need to intercept the click event on md-menu-content. From there you can handle if the event propagates. You can call stopPropagation immediately and handle click events on your own.

I actually replicated md-menu's capture logic. What's interesting is you can do this

angular.element(target).triggerHandler('click');

and it'll call ng-click but not fire md-menu's click capture.

This is a workaround that uses md-menu-disable-close but at least you don't have to fork it for to work. Hopefully somebody will write in support for this in the future.

Thanks @clshortfuse I like your workaround for now

@clshortfuse

Your solutions works great! One minor issue though. Could you explain the purpose of
&& (!closestMenu || closestMenu == opts.parent[0]) equates to false 100% of time for my usage since it always returns an element as it is in a md-menu and opts is undefined. I've removed this for my usage but just curious.

My structure is similar to md-menu -> md-menu-content -> div/directive -> div -> md-menu-item -> md-button.ng-click

@brianpkelley your first element in md-menu-content must be a md-menu-item

So, just add this as your first element:

<md-menu-item ng-show="false"><p></p></md-menu-item>

has anyone already made the restrict: 'A' directive which will work as does clshortfuse's ?:

usage would be something like:

<md-menu-item>
   <md-button
         ng-click="wontBreak()"
         md-menu-disable-close="md-menu-disable-close"
    >Don't break</md-button>
</md-menu-item>

@clshortfuse Your code is great, but every time you open menu, new click event added to menu items.

The md-menu component looks for clicks from elements that have one of these attributes ['ng-click', 'ng-href', 'ui-sref']. So, you can work around this issue by creating your own version of ng-click, and using that for any click events inside md-menu, like so:

app.directive('myClick', function ($parse, $rootScope) {
    return {
        restrict: 'A',
        compile: function ($element, attrs) {
            var fn = $parse(attrs.myClick, null, true);
            return function myClick(scope, element) {
                element.on('click', function (event) {
                    var callback = function () {
                        fn(scope, { $event: event });
                    };
                    scope.$apply(callback);
                })
            }
        }
    }
})

...and in your html...

<md-menu>
    <md-button>Open Menu</md-button>
    <md-menu-content>
        <md-button my-click="doSomething()">Click me without closing the menu</button>
    </md-menu-content>
</md-menu>

It's not ideal, but it'll get you by for now.

+1

Hi,

So I had a use-case, where I needed md-date-picker in md-menu, but it closes when I click on datepicker. Any workaround for this?

Has the fix gone live with the current release - 1.0.5, it is not fixed in 1.0.4.

Please look at this codepen.

That's an interesting edge case. The fix has gone live, but it won't work for md-date-picker, since you can't place the md-prevent-menu-close attribute on a button inside the md-select control.

I suggested a fix in a separate issue: https://github.com/angular/material/issues/6364

Solved this with the following: Assign onMouseDown event and add event.stopPropagation() to it.

Solved this using ngMouseup instead of ngClick. No stopPropagation() required.

As an update, you can use the md-prevent-menu-close attribute:

<md-button ng-click="clicked()" md-prevent-menu-close="md-prevent-menu-close"></md-button>

Documentation

Was this page helpful?
0 / 5 - 0 ratings