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запуск события, когда я нажимаю кнопку удаления в ячейке таблицы?

Ответы:


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();
  }
  ...
};

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();
            });
        }
   };
});

Ницца! Я переименовал вашу директиву в 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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.