How can I make an AngularJS directive to stopPropagation?
Asked Answered
M

4

76

I am trying to "stopPropagation" to prevent a Twitter Bootstrap navbar dropdown from closing when an element (link) inside an li is clicked. Using this method seems to be the common solution.

In Angular, seems like a directive is the place to do this? So I have:

// do not close dropdown on click
directives.directive('stopPropagation', function () {
    return {
        link:function (elm) {            
            $(elm).click(function (event) {                
                event.stopPropagation();
            });
        }
    };
});

... but the method does not belong to element:

TypeError: Object [object Object] has no method 'stopPropagation'

I tie in the directive with

<li ng-repeat="foo in bar">
  <div>
    {{foo.text}}<a stop-propagation ng-click="doThing($index)">clickme</a>
  </div>
</li>

Any suggestions?

Mil answered 27/1, 2013 at 5:34 Comment(3)
You should make some room in the tags for javascript (or even jquery).Lawabiding
Good suggestion. Made the edit.Mil
The reason the event.stopPropagation() doesn't work in your code is that AngularJS and jQuery have two separate event cycles. That's one reason why it's generally a bad idea to use them both. Your click event defined with ngClick uses Angular, but you're trying to use jQuery to stop the event propagation.Germane
M
123

"Currently some directives (i.e. ng:click) stops event propagation. This prevents interoperability with other frameworks that rely on capturing such events." - link

... and was able to fix without a directive, and simply doing:

<a ng-click="doThing($index); $event.stopPropagation();">x</a>
Mil answered 27/1, 2013 at 6:25 Comment(5)
This was perfect. I had a dropdown with a form in it. I added ng-click="$event.stopPropagation()" to the div around the form and that fixed the dropdown from going away when the form inputs were clicked. Thanks!Despiteful
simple solution. worked perfectly. I would prefer this rather than creating a directive.Morningglory
Indeed, simplier than having to create a directive. works fine for me. ThanksMontreal
Simple for a single-use scenario. I'd recommend using a directive to anyone who needs to use this code in multiple places throughout the view - for one, it's easier to extend the functionality, handle more events, and especially to try to keep logic out of the markup as much as possible. Cheers!Blinkers
This strategy does not works if we click on another button,link.The current link does not gets closedShapiro
G
156

I've used this way: Created a directive:

    .directive('stopEvent', function () {
        return {
            restrict: 'A',
            link: function (scope, element, attr) {
                if(attr && attr.stopEvent)
                    element.bind(attr.stopEvent, function (e) {
                        e.stopPropagation();
                    });
            }
        };
     });

that could be used this way:

<a ng-click='expression' stop-event='click'>

This is more generic way of stopping propagation of any kind of events.

Gardol answered 27/1, 2013 at 12:18 Comment(11)
BTW, can you elaborate on the restrict keyword? Not finding a good resource for what that means exactly.Mil
Sure! docs.angularjs.org/guide/directive "Directive Definition Object": restrict - String of subset of EACM which restricts the directive to a specific directive declaration style. If omitted directives are allowed on attributes only.Gardol
This is an interesting approach... I like it. Thanks!Mina
Same solution for .prefentDefault() in IE7 to keep page reloads from happening. Great Solution!Martines
Oh lord i love directivesJillayne
This does not work for me in Safari Mobile iOS 7. But the $event.stopProgation(); works.Heptarchy
var to = attr.ngStopEvent != "*" ? attr.ngStopEvent : 'blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error'; // to support wildcard in element.bind(to,Sum
Yo, I added an if(attr && attr.stopEvent) check just to make things a little more safe. Let me know if this isn't cool. Hope it helps :]. Thanks for the great snippetBlinkers
I used this solution; it works fine and it seems more "clean" for me than the selected answer.Stilbestrol
Thank you so much! You save my life! :) I used it to scroll content inside of angular material design bottomSheet : material.angularjs.org/latest/demo/bottomSheet Here is how i used it: <div class='creditdebitcard-scroll' stop-event='touchstart'>Hippel
I have a checkbox and a select inside the heading of an accordion. When pressing on the checkbox or the select, the accordion opened or closed (not the desired effect). I solved this by adding ng-click="$event.stopImmediatePropagation();" to the checkbox and ng-click="$event.stopImmediatePropagation();$event.preventDefault();" to the select, and now everything Works as expected.Handout
M
123

"Currently some directives (i.e. ng:click) stops event propagation. This prevents interoperability with other frameworks that rely on capturing such events." - link

... and was able to fix without a directive, and simply doing:

<a ng-click="doThing($index); $event.stopPropagation();">x</a>
Mil answered 27/1, 2013 at 6:25 Comment(5)
This was perfect. I had a dropdown with a form in it. I added ng-click="$event.stopPropagation()" to the div around the form and that fixed the dropdown from going away when the form inputs were clicked. Thanks!Despiteful
simple solution. worked perfectly. I would prefer this rather than creating a directive.Morningglory
Indeed, simplier than having to create a directive. works fine for me. ThanksMontreal
Simple for a single-use scenario. I'd recommend using a directive to anyone who needs to use this code in multiple places throughout the view - for one, it's easier to extend the functionality, handle more events, and especially to try to keep logic out of the markup as much as possible. Cheers!Blinkers
This strategy does not works if we click on another button,link.The current link does not gets closedShapiro
L
9

stopPropagation has to be called on an event object, not the element itself. Here's an example:

compile: function (elm) {
    return function (scope, elm, attrs) {
        $(elm).click(function (event) {
            event.stopPropagation();
        });
    };
}
Lawabiding answered 27/1, 2013 at 5:36 Comment(2)
Thanks. I edited the question so that the directive calls stopPropagation on event and not element. Didn't work. Found this: "Currently some directives (i.e. ng:click) stops event propagation. This prevents interoperability with other frameworks that rely capturing such events." - github.com/angular/angular.js/issues/259Mil
So found this to be the fix: <a ng-click="doThing($index); $event.stopPropagation();">x</a>Mil
M
1

Here's a simple, abstract directive to stop event propagation. I figure it might be useful to someone. Simply pass in the event you wish to stop.

<div stopProp="click"></div>

app.directive('stopProp', function () {
  return function (scope, elm, attrs) {
    elm.on(attrs.stopProp, function (event) {
        event.stopPropagation();
    });
  };
});
Mirilla answered 6/11, 2015 at 16:54 Comment(1)
wouldn't that need to be stop-prop in the element?Strum

© 2022 - 2024 — McMap. All rights reserved.