AngularJS ng-click stopPropagation


433

Ho un evento Click su una riga della tabella e in questa riga c'è anche un pulsante Elimina con un evento Click. Quando faccio clic sul pulsante Elimina, viene attivato anche il clic Evento sulla riga.

Ecco il mio codice

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

Come posso evitare che l' showUserevento venga attivato quando faccio clic sul pulsante Elimina nella cella della tabella?

Risposte:


801

La direttiva ngClick (così come tutte le altre direttive sugli eventi) crea una $eventvariabile disponibile sullo stesso ambito. Questa variabile è un riferimento eventall'oggetto JS e può essere utilizzata per chiamare 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
Non sono riuscito a capire se questo è disponibile nel codice del controller - $ scope. L'evento $ non sembra funzionare. Qualche idea?
user1338062,

93
oggetto @event si crea all'interno della direttiva ng-click, ed è disponibile a voi per passarlo a vostra ng-clickfunzione del gestore: ng-click="deleteUser(user.id, $event)".
Stewie,

6
Grazie, ho pensato che fosse quello, ma penso che
puzzi

2
Penso che sia un antipasto eseguire più espressioni come questa. Perché non passare $ event come terzo argomento a deleteUser () e quindi stopPropagation () all'interno di quella funzione?
djheru,

18
Ho dovuto aggiungere $event.preventDefault()anche io , altrimenti chiamare $event.stopPropagation()mi stava reindirizzando alla radice della mia app quando si fa clic sul pulsante.
Desty,

124

Un'aggiunta alla risposta di Stewie. Nel caso in cui il callback decida se interrompere o meno la propagazione, ho trovato utile passare l' $eventoggetto al callback:

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

E quindi nel callback stesso, puoi decidere se interrompere la propagazione dell'evento:

$scope.childHandler = function ($event) {
  if (wanna_stop_it()) {
    $event.stopPropagation();
  }
  ...
};

10
questo è più elegante di eseguire espressioni multiple nell'attributo html, grazie
Eason,

3
Ricorda di inserire l'argomento "$ event", nella direttiva html ng-click. All'inizio mi sono imbattuto in questo.
ThinkBonobo,

1
Per uno stop immediato meglio usare $ event.stopImmediatePropagation ()
Edgar Chavolla

Così semplice, ma così ignorato. Stavo guardando la risposta scelta e pensando "davvero? QUESTO brutto?". Non ho nemmeno realizzato di passarlo come parametro. Veramente bello.
Tfrascaroli,

10

Ho scritto una direttiva che ti consente di limitare le aree in cui un clic ha effetto. Potrebbe essere utilizzato per alcuni scenari come questo, quindi invece di dover gestire il clic caso per caso, puoi semplicemente dire "i clic non verranno fuori da questo elemento".

Lo useresti così:

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

Tieni presente che ciò impedirebbe tutti i clic sull'ultima cella, non solo il pulsante. Se non è quello che vuoi, potresti voler avvolgere il pulsante in questo modo:

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

Ecco il codice della direttiva:

angular.module('awesome', []).directive('isolateClick', function() {
    return {
        link: function(scope, elem) {
            elem.on('click', function(e){
                e.stopPropagation();
            });
        }
   };
});

Bello! Ho rinominato la tua direttiva in ngClickquanto in realtà non voglio mai propagare un evento già gestito.
maaartinus,

1
Stai attento con quello. Alcuni altri plugin potrebbero effettivamente voler ascoltare quei clic. Se si utilizzano descrizioni comandi che si chiudono quando si fa clic all'esterno, potrebbero non chiudersi mai, poiché i clic esterni non vengono propagati al corpo.
Jens,

Questo è un buon punto, ma questo problema potrebbe verificarsi anche con la tua direttiva. Forse dovrei solo aggiungere una proprietà simile ignoreNgClick=trueall'evento e gestirla ... in qualche modo. Idealmente, nella ngClickdirettiva originale , ma modificarlo sembra sporco.
maaartinus,

Sì, ecco perché non userei questa direttiva se non ne avessi davvero bisogno. Una volta ho dovuto persino fare un'altra direttiva per continuare la propagazione dei clic proprio per questo motivo. Quindi avevo una direttiva isolate-click, poi un paio di genitori avevo un'altra direttiva continua-click. In questo modo il clic è stato ignorato solo per gli elementi centrali.
Jens,

1

Nel caso in cui si stia utilizzando una direttiva come me, è così che funziona quando è necessario il binding dei due dati, ad esempio dopo aver aggiornato un attributo in qualsiasi modello o raccolta:

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

Ora, puoi facilmente usarlo in qualsiasi pulsante, collegamento, div, ecc. In questo modo:

<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
Aggiungeresti qualche spiegazione del perché pensi che questo risponda alla domanda?
paisanco,

In un certo senso risponde alla domanda. Mostra come passare l'evento al callback che è più di quello che la domanda iniziale ha capito.
Hewstone,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.