【问题标题】:AngularJS: Dynamically binding Click in directive gets fired multiple timesAngularJS:动态绑定 Click in 指令被多次触发
【发布时间】:2016-02-17 06:25:43
【问题描述】:

我正在尝试动态添加字段,并在指令的 link 函数中绑定 click 事件。但随着我添加更多字段,它似乎被触发了几次。请参阅下面的示例 -

var clicks = 0;
var app = angular.module('test', []);

app.directive('control', function($compile) {
  var linker = function(scope, element, attrs) {
    element.html('<button style="background:grey;">Button ' + scope.count + '</button>');

    element.bind('click', function() {
      clicks++;
      $('#clicks').html('Clicked ' + clicks + ' times')
    });

    $compile(element.contents())(scope);
  };

  return {
    restrict: 'E',
    scope: {
      count: '@'
    },
    link: linker
  }
});

app.controller('TestController', function($scope, $compile) {
  $scope.count = 1;
  $scope.addControl = function() {
    $('#content').append('<control count="' + $scope.count+++'"></control>');
    $compile($('#content').contents())($scope);
  };
});
#content {
  margin-top: 10px;
}
#clicks {
  margin-top: 10px;
}
p {
  color: grey;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test" ng-controller="TestController">
  <button ng-click="addControl()">Add Button</button>
  <div id="content"></div>
  <div id="clicks"></div>
</div>

<p>Add multiple buttons and click the button that was added in the beginning. Notice the Clicks to be increased multiple times.</p>
<p>For instance, add a bunch of buttons and click Button 1</p>

我想让click 事件只针对特定元素触发一次。

【问题讨论】:

    标签: javascript jquery angularjs angularjs-directive angularjs-scope


    【解决方案1】:

    问题是因为您多次编译#content DOM,因此您的旧元素再次获得绑定点击事件。如果您仔细观察,您会发现您的nth 按钮绑定了n-1 点击事件。

    下面是解释。

    1. 当您添加第一个按钮时,它会添加并编译第一个按钮。

      • 现在#content 有 1 个按钮,该按钮绑定了 1 个点击事件。
    2. 当您添加第二个按钮时,它会添加到 DOM 中,但它会重新编译整个 #content DOM,您知道它已经有 1 个带有点击事件的按钮。当您#content DOM 时,它将再次重新编译第一个指令并再次向其添加点击事件。它还将单击事件到第二个按钮。

      • 现在#content 有 2 个按钮
      • 第一个按钮绑定了 2 个事件
      • 第二个按钮绑定了 1 个事件
    3. 当你添加第三个按钮时,你会看到下面的变化

      • 现在#content 有 2 个按钮
      • 第一个按钮绑定了 3 个事件
      • 第二个按钮绑定了 2 个事件
      • 第三个按钮绑定了 1 个事件

    我会说不要通过每次重新编译 DOM 自己在表单上呈现 controls。假设您已经添加了对页面的第 100 个控件,因为您正在无缘无故地重新编译 99 控件,从技术上讲这没有意义。所以最好你应该把渲染控件的责任交给ng-repeat

    标记

    <div ng-controller="TestController">
        <button ng-click="addControl()">Add Button</button>
        <div ng-repeat="control in controls"><control count="{{control}}"></control></div>
        <div id="clicks"></div>
    </div>
    

    控制器

    app.controller('TestController', function($scope, $compile) {
      $scope.count = 1;
      $scope.controls = [];
      $scope.controlsCount = 0;
      $scope.addControl = function() {
        $scope.controls.push(++$scope.controlsCount);
      };
    });
    

    Demo plunkr

    【讨论】:

    • 为什么我的回答没有得到标记/赞成?有什么想法/建议吗?
    • downvoter 我可以知道downvote背后的原因吗?请务必添加评论,以便我知道那里有什么问题,以改进我。
    • @MedetTleukabiluly 问题是我每次创建新控件时都在编译#content(正如Pankaj 指出的那样)。尽管解除绑定有效,但我认为这不是我问题的实际解决方案。
    • @AswinRamakrishnan 但我不明白为什么我的回答会被否决?你有什么想法吗:(
    【解决方案2】:

    你绑定click事件两次, 因为指令在链接阶段被初始化两次,点击处理程序被绑定,你触发了摘要循环 $compile(element.contents())(scope); 将事件附加到指令,因为它们处于链接阶段,但事件可以绑定多次,所以指令有多个点击事件,第一个建议是unbind event first

    element.unbind('click').bind('click');
    

    你可能会问为什么那个元素可能有多个点击事件,这是怎么回事

    //event1
    document.body.onclick = function(){ console.log('event1'); }
    
    //event2
    var oldClick = document.body.onclick;
    document.body.onclick = function(){ console.log('event2'); oldClick.bind(document.body).call(); }
    
    //this will trigger
    event2
    event1
    

    下面的工作示例

    var clicks = 0;
    var app = angular.module('test', []);
    
    app.directive('control', function($compile) {
      var linker = function(scope, element, attrs) {
        element.html('<button style="background:grey;">Button ' + scope.count + '</button>');
        element.unbind('click').bind('click', function() {
          clicks++;
          $('#clicks').html('Clicked ' + clicks + ' times')
        });
        $compile(element.contents())(scope);
      };
    
      return {
        restrict: 'E',
        scope: {
          count: '@'
        },
        link: linker
      }
    });
    
    app.controller('TestController', function($scope, $compile) {
      $scope.count = 1;
      $scope.addControl = function() {
        $('#content').append('<control count="' + $scope.count+++'"></control>');
        $compile($('#content').contents())($scope);
      };
    });
    #content {
      margin-top: 10px;
    }
    #clicks {
      margin-top: 10px;
    }
    p {
      color: grey;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
    <div ng-app="test" ng-controller="TestController">
      <button ng-click="addControl()">Add Button</button>
      <div id="content"></div>
      <div id="clicks"></div>
    </div>
    
    <p>Add multiple buttons and click the button that was added in the beginning. Notice the Clicks to be increased multiple times.</p>
    <p>For instance, add a bunch of buttons and click Button 1</p>

    【讨论】:

    • 你只有element.unbind('click').bind('click'); 这部分是正确的..但是解释不是重点..你能看看我的答案..你会更好地了解它为什么会发生。谢谢:-)
    • 我确实编辑了我的答案,没有更多细节。如果您有任何不明白的地方,请告诉我。
    • 我是说你对我的回答的编辑会很棒;)
    【解决方案3】:

    你需要从角度思考。

    像这样重写你的链接函数

    var linker = function(scope, element, attrs) {
     element.html('<button ng-click="onClick()"  style="background:grey;">Button ' + scope.count + ' Clicked {{clicks}} times</button>');
    
     scope.clicks=0;
     scope.onClick = function(){
      scope.clicks+;
     }
     $compile(element.contents())(scope);
    };
    

    【讨论】:

    • 我不认为这是解决方案。我明白我做错了什么。每次我添加一个控件时,我都会重新编译它的父级的内容。
    • 我认为,您需要以angular 的方式思考。在angular 代码中使用jQuery不好的 做法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-31
    • 1970-01-01
    • 2017-11-01
    • 1970-01-01
    • 2014-02-14
    • 2016-06-12
    • 2021-09-03
    相关资源
    最近更新 更多