AngularJS ng-click stopPropagation


433

テーブル行にクリックイベントがあり、この行にはクリックイベントのある削除ボタンもあります。削除ボタンをクリックすると、行のクリックイベントも発生します。

これが私のコードです。

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

showUserテーブルセルの削除ボタンをクリックしたときにイベントが発生しないようにするにはどうすればよいですか?

回答:


801

ngClickディレクティブ(および他のすべてのイベントディレクティブ)は$event、同じスコープで使用可能な変数を作成します。この変数はJS eventオブジェクトへの参照であり、次の呼び出しに使用できます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
これがコントローラーコードで利用可能かどうかはわかりませんでした。$ scope。$ eventが機能していないようです。何か案は?
user1338062 14

93
@eventオブジェクトはng-clickディレクティブ内に作成され、ng-clickハンドラー関数に渡すために使用できますng-click="deleteUser(user.id, $event)"
Stewie 2014

6
ありがとう、ちょっと考えましたが、それでもまだ悪臭を放つと思います:)
user1338062

2
このように複数の式を実行するのはアンチパターンだと思います。$ eventを3番目の引数としてdeleteUser()に渡し、その関数内でstopPropagation()を渡さないのはなぜですか?
djheru

18
$event.preventDefault()も追加する必要がありました。それ以外の場合$event.stopPropagation()は、ボタンをクリックすると、呼び出しによってアプリのルートにリダイレクトされました。
Desty

124

Stewieの回答への追加。伝達を停止するかどうかをコールバックが決定する場合、$eventオブジェクトをコールバックに渡すと便利です。

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

そして、コールバック自体で、イベントの伝播を停止するかどうかを決定できます。

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

10
これは、感謝のHTML属性に複数の式を実行するよりも、よりエレガントである
イーソン

3
html ng-clickディレクティブに「$ event」引数を忘れずに入れてください。私は最初にそれを偶然見つけました。
ThinkBonobo 2015

1
すぐに停止するには$ event.stopImmediatePropagation()を使用してください
Edgar Chavolla

とてもシンプルですが、無視されています。私は選ばれた答えを見て、「本当に?これは醜いですか?」と考えていました。それをパラメーターとして渡すことにさえ気付かなかった。すごくいい。
tfrascaroli

10

クリックが影響する領域を制限できるディレクティブを作成しました。これは、このような特定のシナリオで使用できるため、ケースバイケースでクリックを処理する代わりに、「クリックはこの要素から発生しない」と言うことができます。

次のように使用します。

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

これにより、ボタンだけでなく、最後のセルのすべてのクリックが防止されることに注意してください。それがあなたが望むものではない場合は、次のようにボタンをラップすることができます:

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

ディレクティブのコードは次のとおりです。

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

いいね!ngClickすでに処理されたイベントを実際に伝播したくないので、ディレクティブの名前をに変更しました。
maaartinus 2016年

1
気をつけて。他の一部のプラグインは、実際にそれらのクリックをリッスンしたい場合があります。外側をクリックすると閉じないツールチップを使用すると、外側のクリックが本体に伝達されないため、決して閉じない場合があります。
イェンス

それは良い点ですが、この問題はディレクティブでも起こります。たぶん私はignoreNgClick=trueイベントのようなプロパティを追加してそれを処理する必要があります...どういうわけか。理想的には、元のngClickディレクティブでは、それを変更することは汚いように聞こえます。
maaartinus 2016年

はい、そうです。それが本当に必要な場合を除いて、このディレクティブを使用しない理由です。このため、クリックの伝播を続行するために別のディレクティブを作成する必要がありました。それで、isolate-clickディレクティブがあり、その後、親のカップルがもう1つのcontinue-clickディレクティブを持っていました。このようにして、クリックは中央の要素に対してのみスキップされました。
イェンス

1

私のようなディレクティブを使用している場合、これは、たとえば、任意のモデルまたはコレクションの属性を更新した後で、2つのデータウェイバインディングが必要な場合の動作です。

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

これで、ボタン、リンク、divなどで簡単に使用できます。

<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
これが質問に答えると思う理由を説明してください。
パイサンコ2015

それは一種の質問に答えます。これは、イベントをコールバックに渡す方法を示しています。これは、最初の質問が理解した以上のものです。
hewstone 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.