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?

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


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();
  }
  ...
};

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

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>

-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
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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.