AngularJS ng-click stopPropagacja

433

Mam zdarzenie kliknięcia w wierszu tabeli, aw tym wierszu znajduje się również przycisk usuwania ze zdarzeniem kliknięcia. Kiedy klikam przycisk Usuń, klikane jest również zdarzenie w wierszu.

Oto mój kod.

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

Jak mogę zapobiec showUseruruchamianiu zdarzenia po kliknięciu przycisku usuwania w komórce tabeli?

michael_knight
źródło

Odpowiedzi:

801

Dyrektywa ngClick (podobnie jak wszystkie inne dyrektywy dotyczące zdarzeń) tworzy $eventzmienną, która jest dostępna w tym samym zakresie. Ta zmienna jest odniesieniem do eventobiektu JS i może być używana do wywoływania 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

Stewie
źródło
2
Nie mogłem się zorientować, czy jest to dostępne w kodzie kontrolera - $ scope. $ Zdarzenie wydaje się nie działać. Jakieś pomysły?
user1338062,
93
@event obiekt jest tworzony wewnątrz dyrektywy ng-click, i jest ona dostępna do was przekazać je do ng-clickfunkcji obsługi: ng-click="deleteUser(user.id, $event)".
Stewie
6
Dzięki, pomyślałem, że to jeden, ale myślę, że wciąż śmierdzi :)
user1338062 21.02. O
2
Wydaje mi się, że wykonywanie wielu wyrażeń w ten sposób jest anty-wzorcem. Dlaczego po prostu nie przekazać $ event jako trzeciego argumentu do deleteUser (), a następnie stopPropagation () wewnątrz tej funkcji?
djheru
18
Musiałem $event.preventDefault()też dodać , w przeciwnym razie wywołanie $event.stopPropagation()przekierowało mnie do katalogu głównego mojej aplikacji po kliknięciu przycisku.
Desty
124

Dodatek do odpowiedzi Stewiego. W przypadku, gdy Twoje wywołanie zwrotne decyduje, czy propagacja powinna zostać zatrzymana, uznam za użyteczne przekazanie $eventobiektu do wywołania zwrotnego:

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

A następnie w samym wywołaniu zwrotnym możesz zdecydować, czy propagacja zdarzenia powinna zostać zatrzymana:

$scope.childHandler = function ($event) {
  if (wanna_stop_it()) {
    $event.stopPropagation();
  }
  ...
};
korya
źródło
10
jest to bardziej eleganckie niż wykonywanie wielu wyrażeń w atrybucie HTML, dzięki
Eason
3
Pamiętaj, aby umieścić argument „$ event” w dyrektywie html ng-click. Na początku natknąłem się na to.
ThinkBonobo
1
Do natychmiastowego zatrzymania lepiej użyj $ event.stopImmediatePropagation ()
Edgar Chavolla
Tak proste, ale tak ignorowane. Patrzyłem na wybraną odpowiedź i myślałem „naprawdę? TO brzydkie?”. Nawet nie zdawałem sobie sprawy z przekazania go jako parametru. Bardzo miłe.
tfrascaroli
10

Napisałem dyrektywę, która pozwala ograniczyć obszary, w których kliknięcie ma wpływ. Może być stosowany w niektórych scenariuszach, takich jak ten, więc zamiast zajmować się klikaniem w poszczególnych przypadkach, możesz po prostu powiedzieć „kliknięcia nie wyjdą z tego elementu”.

Użyłbyś tego w ten sposób:

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

Pamiętaj, że zapobiegnie to wszelkim kliknięciom ostatniej komórki, a nie tylko przycisku. Jeśli nie tego chcesz, możesz zawinąć przycisk w ten sposób:

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

Oto kod dyrektywy:

angular.module('awesome', []).directive('isolateClick', function() {
    return {
        link: function(scope, elem) {
            elem.on('click', function(e){
                e.stopPropagation();
            });
        }
   };
});
Jens
źródło
Miły! Zmieniłem nazwę twojej dyrektywy na ngClick, ponieważ tak naprawdę nigdy nie chcę propagować już obsłużonego zdarzenia.
maaartinus
1
Ostrożnie z tym. Niektóre inne wtyczki mogą chcieć odsłuchiwać te kliknięcia. Jeśli użyjesz podpowiedzi, które zamykają się podczas klikania na zewnątrz, mogą nigdy nie zostać zamknięte, ponieważ kliknięcia zewnętrzne nie są propagowane do treści.
Jens
To dobra uwaga, ale ten problem zdarzyłby się również w przypadku twojej dyrektywy. Może powinienem po prostu dodać właściwość podobną ignoreNgClick=truedo wydarzenia i obsłużyć ją ... jakoś. Idealnie, w pierwotnej ngClickdyrektywie, ale modyfikacja go zabrzmi.
maaartinus
Tak, właśnie dlatego nie używałbym tej dyrektywy, chyba że naprawdę jej potrzebuję. Kiedyś musiałem wydać kolejną dyrektywę, aby kontynuować propagację kliknięć z tego właśnie powodu. Miałem więc dyrektywę izolowania kliknięcia, a potem kilku rodziców w górę miałem kolejną dyrektywę kontynuowania kliknięcia. W ten sposób kliknięcie zostało pominięte tylko dla środkowych elementów.
Jens
1

W przypadku korzystania z dyrektywy takiej jak ja działa to tak, gdy potrzebujesz powiązania dwóch danych, na przykład po zaktualizowaniu atrybutu w dowolnym modelu lub kolekcji:

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

Teraz możesz go łatwo używać w dowolnym przycisku, linku, div itp. W następujący sposób:

<button set-survey-in-edition-mode >Edit survey</button>
heriberto perez
źródło
-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;
      }
   };

Hems
źródło
7
Czy dodałbyś jakieś wyjaśnienie, dlaczego Twoim zdaniem odpowiada to pytanie?
paisanco,
To rodzaj odpowiedzi na pytanie. Pokazuje, jak przekazać zdarzenie do oddzwaniania, co jest więcej niż początkowe pytanie.
hewstone