Angularjs Ng-Click Stoppropagation

AngularJS ng-click stopPropagation

ngClick directive (as well as all other event directives) creates $event variable which is available on same scope. This variable is a reference to JS event object and can be used to call stopPropagation():

<table>
<tr ng-repeat="user in users" ng-click="showUser(user)">
<td>{{user.firstname}}</td>
<td>{{user.lastname}}</td>
<td>
<button class="btn" ng-click="deleteUser(user.id, $index); $event.stopPropagation();">
Delete
</button>
</td>
</tr>
</table>

PLUNKER

Issues with $event.stopPropagation on ng-click

This is a stupid behavior while the optgroup is the parent element and its the height of the child elements in DOM. So, once you moved the cursor while dragging and stop dragging the event ng-click is fired on the optgroup element aswell(because the cursor is upon it). I've made it work by adding a software switch as you can see in this runnable plnkr.

View

<select multiple class="box">
<optgroup ng-click="clickOptGroup(key);" ng-repeat="(key,value) in data" label="{{value.label}}">
<option ng-mousedown="mouseDown()" ng-mouseup="mouseUp()" ng-click="$event.stopPropagation();" ng-if="value.expanded" ng-repeat="id in value.ids">{{id}}</option>
</optgroup>
</select>

AngularJS application

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

app.controller('MainCtrl',function ($scope, $timeout) {

var mouseDown = false;

$scope.data = [{
label: "My Label", ids: [ "one id", "another id" ],
expanded: true
},{
label: "My Other Label", ids: [ "one id", "another id" ],
expanded: false
}];

$scope.clickOptGroup = function (key) {
console.log('in');
if (!mouseDown) {
$scope.data[key].expanded = ! $scope.data[key].expanded;
}
}

$scope.mouseDown = function ($event) {
console.log('mouseDown');
mouseDown = true;
}
$scope.mouseUp = function ($event) {
$timeout(function () {
mouseDown = false;
}, 50);
}
});

AngularJS: How to stop event propagation from ng-click?

In your case you can't stop propagtion because click event happens on the same element, there are just two different handlers.

However you can leverage the fact that this is the same event object in both controller ngClick and in directive. So what you can do is to set some property to this event object and check for it in directive:

$scope.dosomething = function($event){
$event.stopPropagation();
$event.preventDefault();
alert('here');

if (someCondtion) {
$event.stopNextHandler = true;
}
}

and in directive:

link: function(scope, element){
element.bind('click', function(e) {
if (e.stopNextHandler !== true) {
alert('want to prevent this');
}
});
}

Demo: http://jsfiddle.net/5bfkbh7u/6/

AngularJS - href ng-click - event bubbling

To do this you have to use Angular and jQuery event handling. See below code snippet. And working https://plnkr.co/edit/seF391qx3OcUubT8MeO7?p=preview

var app = angular.module('myApp', []);
app.controller('demoController', function($scope, $anchorScroll) {
$scope.list = {}; $scope.list.subItems = ['item1', 'item2', 'item3']; $scope.page = {}; $scope.page.updateSubStatus = function(item) { alert(item); }})
$(document).ready(function(){ $('a span.sub-list-item').on('click', function(event){ console.log(event) event.preventDefault(); })})
<!DOCTYPE html><html ng-app="myApp">
<head> <meta charset="utf-8" /> <title>AngularJS Plunker</title> <script>document.write('<base href="' + document.location + '" />');</script> <link rel="stylesheet" href="style.css" /> <script data-require="angular.js@1.4.x" src="https://code.angularjs.org/1.4.9/angular.js" data-semver="1.4.9"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script> <script src="app.js"></script> </head>
<body ng-controller="demoController"> <div class="sub-list" ng-repeat="item in list.subItems" on-finish-render> <a class="list-row" href="www.google.com" > <span class="list-cell name">item.name</span> <span class="list-cell dt">item.dt</span> <span class="list-cell summary"> <span class="sub-list-item" ng-click="page.updateSubStatus(item)" on-click="disableClick()">Click</span> </span> <span class="list-cell list-row-link-icon"></span> </a> </div></body>
</html>

Stop propagation of underlying ng-click inside a jQuery click event

The dropdown directive binds the click event on the document, but when you click on the row, the event starts propagating from the target element down to the root document node (td -> tr -> table -> document).

So that's why your ng-click handler, that you have on your row, always gets called, even though the directive is "stopping" the bubble on document click.

Solution is to use the useCapture flag when adding the click handler for the document.

After initiating capture, all events of the specified type will be
dispatched to the registered listener before being dispatched to any
EventTarget beneath it in the DOM tree. mdn

Now, to instruct the dropdown directive to use your own handler, you need to change the source of the directive. But it's a third party directive, and you probably don't want to do that, for maintability reasons.

This is where the powerful angular $decorator kicks in. You may use the $decorator to change the source of the third-party module on-the-fly, without actually touching the actual source files.

So, with decorator in place, and with custom event handler on the document node, this is how you can make that dropdown behave:

FIDDLE

var myApp = angular.module('myApp', []);

/**
* Original dropdownToggle directive from ui-bootstrap.
* Nothing changed here.
*/
myApp.directive('dropdownToggle', ['$document', '$location', function ($document, $location) {
var openElement = null,
closeMenu = angular.noop;
return {
restrict: 'CA',
link: function(scope, element, attrs) {
scope.$watch('$location.path', function() { closeMenu(); });
element.parent().bind('click', function() { closeMenu(); });
element.bind('click', function (event) {

var elementWasOpen = (element === openElement);

event.preventDefault();
event.stopPropagation();

if (!!openElement) {
closeMenu();
}

if (!elementWasOpen && !element.hasClass('disabled') && !element.prop('disabled')) {
element.parent().addClass('open');
openElement = element;
closeMenu = function (event) {
if (event) {
event.preventDefault();
event.stopPropagation();
}
$document.unbind('click', closeMenu);
element.parent().removeClass('open');
closeMenu = angular.noop;
openElement = null;
};
$document.bind('click', closeMenu); /* <--- CAUSE OF ALL PROBLEMS ----- */
}
});
}
};
}]);

/**
* This is were we decorate the dropdownToggle directive
* in order to change the way the document click handler works
*/
myApp.config(function($provide){
'use strict';

$provide.decorator('dropdownToggleDirective', [
'$delegate',
'$document',
function ($delegate, $document) {

var directive = $delegate[0];
var openElement = null;
var closeMenu = angular.noop;

function handler(e){
var elm = angular.element(e.target);
if(!elm.parents('.dropdown-menu').length){
e.stopPropagation();
e.preventDefault();
}
closeMenu();
// After closing the menu, we remove the all-seeing handler
// to allow the application click events to work nnormally
$document[0].removeEventListener('click', handler, true);
}

directive.compile = function(){
return function(scope, element) {
scope.$watch('$location.path', closeMenu);
element.parent().bind('click', closeMenu);
element.bind('click', function (event) {

var elementWasOpen = (element === openElement);

event.preventDefault();
event.stopPropagation();

if (!!openElement) {
closeMenu();
}

if (!elementWasOpen && !element.hasClass('disabled') && !element.prop('disabled')) {
element.parent().addClass('open');
openElement = element;
closeMenu = function (event) {
if (event) {
event.preventDefault();
event.stopPropagation();
}
$document.unbind('click', closeMenu);
element.parent().removeClass('open');
closeMenu = angular.noop;
openElement = null;
};

// We attach the click handler by specifying the third "useCapture" parameter as true
$document[0].addEventListener('click', handler, true);
}
});
};
};

return $delegate;
}
]);

});

UPDATE:

Note that the updated custom handler will prevent bubbling unless the target element is the actual drop-down option. This will solve the problem where click event was being prevented even when clicking on the drop-down options.

This still won't prevent the event to bubble down to row (from drop-down option), but this is something that's not in any way related to the drop-down directive. Anyway, to prevent such bubbling you can pass the $event object to ng-click expression function and use that object to stop the even to bubble down to the table row:

<div ng-controller="DropdownCtrl">
<table>
<tr ng-click="clicked('row')">
<td>

<div class="btn-group">
<button type="button" class="btn btn-default dropdown-toggle">
Action <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li ng-repeat="choice in items">
<a ng-click="clicked('link element', $event)">{{choice}}</a>
</li>
</ul>
</div>

</td>
</tr>
</table>
</div>
function DropdownCtrl($scope) {
$scope.items = [
"Action",
"Another action",
"Something else here"
];

$scope.clicked = function(what, event) {
alert(what + ' clicked');
if(event){
event.stopPropagation();
event.preventDefault();
}
}

}

What's the best way to cancel event propagation between nested ng-click calls?

What @JosephSilber said, or pass the $event object into ng-click callback and stop the propagation inside of it:

<div ng-controller="OverlayCtrl" class="overlay" ng-click="hideOverlay()">
<img src="http://some_src" ng-click="nextImage($event)"/>
</div>
$scope.nextImage = function($event) {
$event.stopPropagation();
// Some code to find and display the next image
}

ng-change in a checkbox fired more than one time, because an ng-click over it

You need to stop event propagation on label level. Try this:

<label ng-click="$event.stopPropagation()" ...>

Demo: http://plnkr.co/edit/AjD9GlA3zjxABix6hegg?p=preview

The reason why it happens is that the label (connected to corresponding checkbox) sort of generates one more click event in order to pass click to the input. This click event causes described strange issues, because it still bubbles like normal event (well it is normal event), and hence is detected by ngClick directives.



Related Topics



Leave a reply



Submit