【问题标题】:Angular ng-click event delegationAngular ng-click 事件委托
【发布时间】:2012-12-20 05:03:02
【问题描述】:

因此,如果我有一个 100 li 的 ul,每个 li 中是否应该有 ng-clicks,或者有没有办法将事件绑定到 ul 并将其委托给 li 的那种 jquery 做什么?这会更好还是更糟?我们有 100 场活动还是最终只有一场活动?

【问题讨论】:

标签: angularjs angularjs-ng-repeat angularjs-ng-click


【解决方案1】:

似乎 Angular 不使用中继器进行事件委托。有人打开了issue on github about it。争论的焦点是它是否真的能带来更好的性能。

可能有一种解决方法,但它需要 jQuery。它包括创建一个用于父元素的特殊指令,并在其 dom 节点上注册监听器。

这是一个例子,它传递了一个函数名,当点击子节点时要调用它,还传递了一个选择器来帮助识别要监听的子节点。 由于 Angular 的 jquery 实现只为我们提供了 bind 方法 - 仅限于将事件侦听器注册到实际元素 - 我们需要加载 jQuery 才能访问 ondelegate 方法。

HTML

<ul click-children='fun' selector='li'>
    <li ng-repeat="s in ss" data-index="{{$index}}">{{s}}</li>
</ul>

定义的函数在控制器中定义,它期望传递一个索引

$scope.fun = function(index){
    console.log('hi from controller', index, $scope.ss[index]);      
};

该指令使用$parse 将表达式转换为将从事件侦听器调用的函数。

app.directive('clickChildren', function($parse){
  return {
    restrict: 'A',
    link: function(scope, element, attrs){       
      var selector = attrs.selector;
      var fun = $parse(attrs.clickChildren);   
      element.on('click', selector, function(e){       
        // no need to create a jQuery object to get the attribute 
        var idx = e.target.getAttribute('data-index');        
        fun(scope)(idx);        
      });
    }
  };
});

Plunker:http://plnkr.co/edit/yWCLvRSLCeshuw4j58JV?p=preview


注意:可以使用隔离作用域{fun: '&amp;'} 将函数委托给指令,这值得一看,但这会增加复杂性。

【讨论】:

  • 嘿,非常感谢 jm- 的回答。我也一直很好奇这是否会更好。运行一些 js perfs 来查看 id 我可以及时发现差异。这个解决方案很酷!
  • 您还需要侦听$destroy 事件,以从element 中删除事件侦听器。否则我相信当元素从 DOM 中删除时会发生内存泄漏。
  • 您需要使用e.currentTarget 而不是e.targete.target 是点击下的元素。 e.currentTarget 是事件附加到的元素(甚至是委托事件)。例如,如果 html 是 &lt;a&gt;&lt;span&gt;text&lt;/span&gt;&lt;/a&gt; 并且委托选择器是 a 那么 e.target 将是 span 元素,e.currentTarget 将是 a 元素。
  • @climboid 你跑过那些性能测试了吗?你能说什么呢?是否值得用 Angular 进行事件委托?
  • @Leonardo 不幸的是我没有。鉴于当今世界有 angular 2,我什至不确定它是否值得探索
【解决方案2】:

根据 jm- 的示例,我编写了该指令的更简洁灵活的版本。以为我会分享。归功于 jm- ;)

我的版本尝试将函数名称调用为 $scope[ fn ]( e, data ),或者正常失败。

它从被点击的元素中传递一个可选的 json 对象。这允许您使用 Angular 表达式并将大量属性传递给被调用的方法。

HTML

<ul delegate-clicks="handleMenu" delegate-selector="a">
  <li ng-repeat="link in links">
    <a href="#" data-ng-json='{ "linkId": {{link.id}} }'>{{link.title}}</a>
  </li>
</ul>

Javascript

控制器方法

$scope.handleMenu = function($event, data) {
  $event.preventDefault();
  $scope.activeLinkId = data.linkId;
  console.log('handleMenu', data, $scope);
}

指令构造函数

// The delegateClicks directive delegates click events to the selector provided in the delegate-selector attribute.
// It will try to call the function provided in the delegate-clicks attribute.
// Optionally, the target element can assign a data-ng-json attribute which represents a json object to pass into the function being called. 
// Example json attribute: <li data-ng-json='{"key":"{{scopeValue}}" }'></li>
// Use case: Delegate click events within ng-repeater directives.

app.directive('delegateClicks', function(){
  return function($scope, element, attrs) {
    var fn = attrs.delegateClicks;
    element.on('click', attrs.delegateSelector, function(e){
      var data = angular.fromJson( angular.element( e.target ).data('ngJson') || undefined );
      if( typeof $scope[ fn ] == "function" ) $scope[ fn ]( e, data );
    });
  };
});

如果有人愿意贡献,我很乐意听取反馈。

我没有测试 handleMenu 方法,因为我从更复杂的应用程序中提取了它。

【讨论】:

  • 干得好。请参阅我的改进,它使用了对 georg 对“Parse object dot notation to retrieve a value of an object”的回答的改编。
【解决方案3】:

从上面的 BradGreens 的 delegateClicks 开始,我改编了 some code from georg,它允许我将 handleMenu 函数放置在 $scope 的更深处(例如 $scope.tomato.handleMenu)。

app.directive('delegateClicks', function () {
    return function ($scope, element, attrs) {
        var fn = attrs.delegateClicks.split('.').reduce(function ($scope, p) { return $scope[p] }, $scope); 
        element.on('click', attrs.delegateSelector, function (e) {
            var data = angular.fromJson(angular.element(e.target).data('ngJson') || undefined);
            if (typeof fn == "function") fn(e, data);
        });
    };
});

【讨论】:

    【解决方案4】:

    根据issue on GitHub,委派事件处理没有性能优势。

    只需将ng-click 指令与项目$index$event 一起使用:

    <ul>
       <li ng-repeat="item in collection"
           ng-click="lineClicked(item, $index, $event)">
          {{item}}
       </li>
    </ul>
    
    $scope.lineClicked = function(item, index, event) {
        console.log("line clicked", item, index);
        console.log(event.target)
    });
    

    有关详细信息,请参阅

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-01-12
      • 2016-03-27
      • 1970-01-01
      • 1970-01-01
      • 2013-08-26
      • 2014-08-08
      • 1970-01-01
      相关资源
      最近更新 更多