AngularJS ng-click stopPropagation

433

Ich habe ein Klickereignis in einer Tabellenzeile und in dieser Zeile gibt es auch eine Löschschaltfläche mit einem Klickereignis. Wenn ich auf die Schaltfläche Löschen klicke, wird auch das Klickereignis in der Zeile ausgelöst.

Hier ist mein Code.

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

Wie kann ich verhindern, dass das showUserEreignis ausgelöst wird, wenn ich in der Tabellenzelle auf die Schaltfläche Löschen klicke?

michael_knight
quelle

Antworten:

801

Die ngClick-Direktive (sowie alle anderen Ereignisanweisungen) erstellt eine $eventVariable, die im selben Bereich verfügbar ist. Diese Variable ist eine Referenz auf ein JS- eventObjekt und kann zum Aufrufen von stopPropagation(): verwendet werden.

<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
quelle
2
Ich konnte nicht herausfinden, ob dies im Controller-Code verfügbar ist - $ scope. $ Event schien nicht zu funktionieren. Irgendwelche Ideen?
user1338062
93
Das @ event-Objekt wird in der ng-click-Direktive erstellt und steht Ihnen zur Verfügung, um es an Ihre Handlerfunktion weiterzugeben ng-click: ng-click="deleteUser(user.id, $event)".
Stewie
6
Danke, irgendwie dachte ich mir das, aber ich denke es stinkt immer noch :)
user1338062
2
Ich denke, dass es ein Antimuster ist, mehrere Ausdrücke wie diesen auszuführen. Warum nicht einfach $ event als drittes Argument an deleteUser () und dann stopPropagation () innerhalb dieser Funktion übergeben?
Djheru
18
Ich musste auch hinzufügen $event.preventDefault(), sonst $event.stopPropagation()leitete mich ein Anruf beim Klicken auf die Schaltfläche zum Stammverzeichnis meiner App weiter.
Desty
124

Eine Ergänzung zu Stewies Antwort. Falls Ihr Rückruf entscheidet, ob die Weitergabe gestoppt werden soll oder nicht, fand ich es nützlich, das $eventObjekt an den Rückruf zu übergeben:

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

Und dann können Sie im Rückruf selbst entscheiden, ob die Weitergabe des Ereignisses gestoppt werden soll:

$scope.childHandler = function ($event) {
  if (wanna_stop_it()) {
    $event.stopPropagation();
  }
  ...
};
Korya
quelle
10
Dies ist eleganter als die Ausführung mehrerer Ausdrücke im HTML-Attribut, danke
Eason
3
Denken Sie daran, das Argument '$ event' in die HTML-Anweisung ng-click einzufügen. Ich bin zuerst darauf gestoßen.
ThinkBonobo
1
Für einen sofortigen Stopp verwenden Sie besser $ event.stopImmediatePropagation ()
Edgar Chavolla
So einfach und doch so ignoriert. Ich schaute auf die gewählte Antwort und dachte "wirklich? DIESES Hässliche?". Ich habe nicht einmal bemerkt, dass ich es als Parameter übergeben soll. Wirklich nett.
Tfrascaroli
10

Ich habe eine Direktive geschrieben, mit der Sie die Bereiche einschränken können, in denen ein Klick Auswirkungen hat. Es könnte für bestimmte Szenarien wie dieses verwendet werden. Anstatt sich also von Fall zu Fall mit dem Klicken befassen zu müssen, können Sie einfach sagen, dass "Klicks nicht aus diesem Element herauskommen".

Sie würden es so verwenden:

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

Beachten Sie, dass dadurch alle Klicks auf die letzte Zelle und nicht nur auf die Schaltfläche verhindert werden. Wenn Sie dies nicht möchten, können Sie den Button wie folgt umschließen:

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

Hier ist der Code der Richtlinie:

angular.module('awesome', []).directive('isolateClick', function() {
    return {
        link: function(scope, elem) {
            elem.on('click', function(e){
                e.stopPropagation();
            });
        }
   };
});
Jens
quelle
Nett! Ich habe Ihre Direktive in umbenannt ngClick, da ich ein bereits behandeltes Ereignis eigentlich nie weitergeben möchte.
Maaartinus
1
Sei vorsichtig damit. Einige andere Plugins möchten diese Klicks möglicherweise tatsächlich hören. Wenn Sie QuickInfos verwenden, die beim Klicken nach außen geschlossen werden, werden sie möglicherweise nie geschlossen, da die externen Klicks nicht auf den Körper übertragen werden.
Jens
Das ist ein guter Punkt, aber dieses Problem würde auch bei Ihrer Richtlinie auftreten. Vielleicht sollte ich ignoreNgClick=truedem Event einfach eine Eigenschaft wie hinzufügen und damit umgehen ... irgendwie. Idealerweise in der ursprünglichen ngClickDirektive, aber das Ändern klingt schmutzig.
Maaartinus
Ja, deshalb würde ich diese Richtlinie nur verwenden, wenn ich sie wirklich brauche. Einmal musste ich aus genau diesem Grund sogar eine andere Anweisung machen, um die Klickausbreitung fortzusetzen. Also hatte ich eine Direktive-Click-Direktive, dann hatte ich ein paar Eltern eine andere Continue-Click-Direktive. Auf diese Weise wurde der Klick nur für die mittleren Elemente übersprungen.
Jens
1

Wenn Sie eine Direktive wie mich verwenden, funktioniert dies so, wenn Sie die Zwei-Daten-Bindung benötigen, z. B. nach dem Aktualisieren eines Attributs in einem Modell oder einer Sammlung:

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

Jetzt können Sie es einfach in jeder Schaltfläche, jedem Link, jedem Div usw. wie folgt verwenden:

<button set-survey-in-edition-mode >Edit survey</button>
Heriberto Perez
quelle
-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;
      }
   };

Säume
quelle
7
Würden Sie eine Erklärung hinzufügen, warum dies Ihrer Meinung nach die Frage beantwortet?
Paisanco
Es beantwortet die Frage. Es zeigt, wie das Ereignis an den Rückruf weitergeleitet wird, was mehr ist als die ursprüngliche Frage.
Hewstone