AngularJS ng-click stopPropagation

433

J'ai un événement de clic sur une ligne du tableau et dans cette ligne il y a aussi un bouton de suppression avec un événement de clic. Lorsque je clique sur le bouton Supprimer, le clic Événement sur la ligne est également déclenché.

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

Comment puis-je empêcher le showUserdéclenchement de l' événement lorsque je clique sur le bouton Supprimer dans la cellule du tableau?

michael_knight
la source

Réponses:

801

La directive ngClick (ainsi que toutes les autres directives d'événement) crée une $eventvariable qui est disponible sur la même portée. Cette variable est une référence à un eventobjet JS et peut être utilisée pour appeler 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
la source
2
Je n'ai pas pu déterminer si cela est disponible dans le code du contrôleur - $ scope. $ Event ne semble pas fonctionner. Des idées?
user1338062
93
objet @event est créé dans la directive ng-clic, et il est disponible pour vous de le transmettre à votre ng-clickfonction de gestionnaire: ng-click="deleteUser(user.id, $event)".
Stewie
6
Merci, j'ai pensé à celui-là, mais je pense que ça pue encore :)
user1338062
2
Je pense que c'est un contre-modèle pour exécuter plusieurs expressions comme celle-ci. Pourquoi ne pas simplement passer $ event comme troisième argument à deleteUser () puis stopPropagation () à l'intérieur de cette fonction?
djheru
18
J'ai dû ajouter $event.preventDefault()aussi, sinon appeler $event.stopPropagation()me redirigeait vers la racine de mon application en cliquant sur le bouton.
Desty
124

Un ajout à la réponse de Stewie. Dans le cas où votre rappel décide si la propagation doit être arrêtée ou non, j'ai trouvé utile de passer l' $eventobjet au rappel:

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

Et puis dans le rappel lui-même, vous pouvez décider si la propagation de l'événement doit être arrêtée:

$scope.childHandler = function ($event) {
  if (wanna_stop_it()) {
    $event.stopPropagation();
  }
  ...
};
korya
la source
10
c'est plus élégant que d'exécuter plusieurs expressions dans l'attribut html, merci
Eason
3
N'oubliez pas de mettre l'argument '$ event', dans la directive html ng-click. Je suis tombé dessus au début.
ThinkBonobo
1
Pour un arrêt immédiat, utilisez $ event.stopImmediatePropagation ()
Edgar Chavolla
Si simple, mais tellement ignoré. Je regardais la réponse choisie et je pensais "vraiment? CE moche?". Je n'ai même pas réalisé de le passer en paramètre. Vraiment sympa.
tfrascaroli
10

J'ai écrit une directive qui vous permet de limiter les zones où un clic a un effet. Il pourrait être utilisé pour certains scénarios comme celui-ci, donc au lieu d'avoir à traiter le clic au cas par cas, vous pouvez simplement dire "les clics ne sortiront pas de cet élément".

Vous l'utiliseriez comme ceci:

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

Gardez à l'esprit que cela empêcherait tous les clics sur la dernière cellule, pas seulement sur le bouton. Si ce n'est pas ce que vous voulez, vous pouvez envelopper le bouton comme ceci:

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

Voici le code de la directive:

angular.module('awesome', []).directive('isolateClick', function() {
    return {
        link: function(scope, elem) {
            elem.on('click', function(e){
                e.stopPropagation();
            });
        }
   };
});
Jens
la source
Agréable! J'ai renommé votre directive ngClick, car je ne veux en fait jamais propager un événement déjà géré.
maaartinus
1
Soyez prudent avec ça. Certains autres plugins pourraient vouloir écouter ces clics. Si vous utilisez des info-bulles qui se ferment lorsque vous cliquez à l'extérieur, elles peuvent ne jamais se fermer, car les clics extérieurs ne sont pas propagés au corps.
Jens
C'est un bon point, mais ce problème se produirait également avec votre directive. Peut-être que je devrais simplement ajouter une propriété comme ignoreNgClick=trueà l'événement et le gérer ... en quelque sorte. Idéalement, dans la ngClickdirective d' origine , mais la modifier semble sale.
maaartinus
Oui, c'est pourquoi je n'utiliserais cette directive que si j'en ai vraiment besoin. Une fois, j'ai même dû faire une autre directive pour continuer la propagation des clics pour cette raison même. J'ai donc eu une directive isoler-cliquer, puis quelques parents ont eu une autre directive continuer-cliquer. De cette façon, le clic n'a été ignoré que pour les éléments du milieu.
Jens
1

Dans le cas où vous utilisez une directive comme moi, voici comment cela fonctionne lorsque vous avez besoin de la liaison à deux données, par exemple après la mise à jour d'un attribut dans n'importe quel modèle ou collection:

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

Maintenant, vous pouvez facilement l'utiliser dans n'importe quel bouton, lien, div, etc. comme ceci:

<button set-survey-in-edition-mode >Edit survey</button>
heriberto perez
la source
-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;
      }
   };

Ourlets
la source
7
Pourriez-vous ajouter une explication de la raison pour laquelle vous pensez que cela répond à la question?
paisanco
Cela répond en quelque sorte à la question. Il montre comment passer l'événement au rappel, ce qui est plus que ce que la question initiale avait compris.
hewstone