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.