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
Я не міг зрозуміти, чи це доступно в коді контролера - $ range. $ Подія, схоже, не працює. Будь-які ідеї?
user1338062

93
@event об'єкт створюється всередині директиви нг-клік, і вона доступна вам передати його на вашій ng-clickфункції обробника: ng-click="deleteUser(user.id, $event)".
Стюі

6
Дякую, начебто подумав, що це, але я думаю, він все ще смердить :)
user1338062

2
Я думаю, що це антипатерн виконувати подібні вирази. Чому б просто не передати $ event як третій аргумент до deleteUser (), а потім зупинитиPropagation () всередині цієї функції?
джеру

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, дякую
Ісон

3
Не забудьте поставити аргумент "$ event" у директиві htg ng-click. Я спочатку натрапив на це.
ThinkBonobo

1
Для негайної зупинки краще скористайтеся $ event.stopImmediatePropagation ()
Едгар Чаволла

Так просто, але так ігнорується. Я дивився на обрану відповідь і думав "справді? ЦЕ потворно?". Навіть не зрозумів передавати це як параметр. Дійсно приємно.
tfrascaroli

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

Тепер ви можете легко використовувати його в будь-якій кнопці, посиланні, діві тощо тощо:

<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.