AngularJS ng-click stopPropagation

433

У меня есть событие клика в строке таблицы, и в этой строке также есть кнопка удаления с кликом события. Когда я нажимаю кнопку «Удалить», также запускается событие «Событие» в строке.

Вот мой код

<tbody>
  <tr ng-repeat="user in users" class="repeat-animation" ng-click="showUser(user, $index)">
    <td>{{user.firstname}}</td>
    <td>{{user.lastname}}</td>
    <td>{{user.email}}</td>
    <td><button class="btn red btn-sm" ng-click="deleteUser(user.id, $index)">Delete</button></td>
  </tr>
</tbody>

Как я могу предотвратить showUserзапуск события, когда я нажимаю кнопку удаления в ячейке таблицы?

michael_knight
источник

Ответы:

801

Директива ngClick (как и все другие директивы событий) создает $eventпеременную, которая доступна в той же области видимости. Эта переменная является ссылкой на eventобъект JS и может использоваться для вызова 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

Стьюи
источник
2
Я не мог понять, доступно ли это в коде контроллера - $ scope. $ Event, похоже, не работает. Любые идеи?
user1338062
93
@event объект создается внутри директивы нг-клик, и она доступна вам передать его на вашей ng-clickфункции обработчика: ng-click="deleteUser(user.id, $event)".
Стьюи
6
Спасибо, вроде как понял, но думаю, что все еще воняет :)
user1338062
2
Я думаю, что это антипаттерн для выполнения нескольких выражений, подобных этому. Почему бы просто не передать $ event в качестве третьего аргумента deleteUser (), а затем stopPropagation () внутри этой функции?
Джеру
18
Мне тоже пришлось добавить $event.preventDefault(), иначе $event.stopPropagation()при нажатии кнопки при звонке меня перенаправляли в корень моего приложения.
Дести
124

Дополнение к ответу Стьюи. В случае, когда ваш обратный вызов решает, должно ли распространение быть остановлено или нет, я счел полезным передать $eventобъект обратному вызову:

<div ng-click="parentHandler($event)">
  <div ng-click="childHandler($event)">
  </div>
</div>

И затем в самом обратном вызове вы можете решить, следует ли остановить распространение события:

$scope.childHandler = function ($event) {
  if (wanna_stop_it()) {
    $event.stopPropagation();
  }
  ...
};
korya
источник
10
это более элегантно, чем выполнять несколько выражений в атрибуте html, спасибо
Eason
3
Не забудьте указать аргумент $ event в директиве html ng-click. Сначала я наткнулся на это.
ThinkBonobo
1
Для немедленной остановки лучше использовать $ event.stopImmediatePropagation ()
Эдгар Чаволла
Так просто, но так игнорируется. Я смотрел на выбранный ответ и думал: «Действительно? ЭТО УЖАСНО?». Даже не понял, чтобы передать его в качестве параметра. Действительно мило.
Тфраскароли
10

Я написал директиву, которая позволяет ограничивать области, в которых действует клик. Он может быть использован для определенных сценариев, подобных этому, поэтому вместо того, чтобы иметь дело с кликом в каждом конкретном случае, вы можете просто сказать, что «щелчки не будут исходить из этого элемента».

Вы бы использовали это так:

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

Имейте в виду, что это предотвратит все нажатия на последнюю ячейку, а не только на кнопку. Если это не то, что вы хотите, вы можете обернуть кнопку следующим образом:

<span isolate-click>
    <button class="btn" ng-click="deleteUser(user.id, $index);">
        Delete
    </button>
</span>

Вот код директивы:

angular.module('awesome', []).directive('isolateClick', function() {
    return {
        link: function(scope, elem) {
            elem.on('click', function(e){
                e.stopPropagation();
            });
        }
   };
});
Jens
источник
Ницца! Я переименовал вашу директиву в ngClick, поскольку я на самом деле никогда не хочу распространять уже обработанное событие.
Maaartinus
1
Будьте осторожны с этим. Некоторые другие плагины могут действительно хотеть слушать эти щелчки. Если вы используете всплывающие подсказки при щелчке снаружи, они могут никогда не закрыться, потому что внешние щелчки не распространяются на тело.
Дженс
Это хороший момент, но эта проблема может возникнуть и с вашей директивой. Может быть, я должен просто добавить свойство как ignoreNgClick=trueк событию и обработать его ... как-нибудь. В идеале, в оригинальной ngClickдирективе, но изменение звучит грязно.
Maaartinus
Да, это так, поэтому я бы не стал использовать эту директиву, если бы она мне действительно не понадобилась. Однажды мне даже пришлось сделать еще одну директиву для продолжения распространения кликов по этой самой причине. Итак, у меня была директива «Изолировать щелчок», а потом пара родителей, у меня была еще одна директива «Продолжить щелчок». Таким образом, щелчок пропускался только для средних элементов.
Дженс
1

В случае, если вы используете директиву, подобную мне, вот как это работает, когда вам нужно двухстороннее связывание, например, после обновления атрибута в любой модели или коллекции:

angular.module('yourApp').directive('setSurveyInEditionMode', setSurveyInEditionMode)

function setSurveyInEditionMode() {
  return {
    restrict: 'A',
    link: function(scope, element, $attributes) {
      element.on('click', function(event){
        event.stopPropagation();
        // In order to work with stopPropagation and two data way binding
        // if you don't use scope.$apply in my case the model is not updated in the view when I click on the element that has my directive
        scope.$apply(function () {
          scope.mySurvey.inEditionMode = true;
          console.log('inside the directive')
        });
      });
    }
  }
}

Теперь вы можете легко использовать его в любой кнопке, ссылке, div и т. Д., Например так:

<button set-survey-in-edition-mode >Edit survey</button>
Хериберто Перес
источник
-14
<ul class="col col-double clearfix">
 <li class="col__item" ng-repeat="location in searchLocations">
   <label>
    <input type="checkbox" ng-click="onLocationSelectionClicked($event)" checklist-model="selectedAuctions.locations" checklist-value="location.code" checklist-change="auctionSelectionChanged()" id="{{location.code}}"> {{location.displayName}}
   </label>



$scope.onLocationSelectionClicked = function($event) {
      if($scope.limitSelectionCountTo &&         $scope.selectedAuctions.locations.length == $scope.limitSelectionCountTo) {
         $event.currentTarget.checked=false;
      }
   };

Рубцы
источник
7
Не могли бы вы объяснить, почему вы думаете, что это отвечает на вопрос?
Paisanco
Это как бы отвечает на вопрос. Он показывает, как передать событие в обратный вызов, что больше, чем предполагалось в первоначальном вопросе.
Hewstone