【问题标题】:AngularJs Directive: How to dynamically set attributes on ng-repeat elementAngularJs 指令:如何在 ng-repeat 元素上动态设置属性
【发布时间】:2016-02-13 01:44:00
【问题描述】:

我对 AngularJS 比较陌生。在尝试创建指令时,我可以解决这个问题:当使用“ng-repeat”动态添加指令元素的子元素时,如何动态添加/删除这些子元素的属性?

首先,我想到了这个解决方案:

模板

...
a.list-group-item(ng-repeat='playlist in playlists', ng-click='addToPlaylist(playlist, track)', ng-href='playlist/{{ playlist._id }})
...

*指令

link: function(scope, elm, attrs) {   
  var listItems = angular.element(element[0].getElementsByClassName('list-group-item')
  angular.forEach(listItems, function(item, index) {
    'add' in attrs ? item.removeAttr('href') : item.removeAttr('ng-click');
    listItems[index] = item;
  }
... 

结果

事实证明,我的代码永远不会进入这个 angular.forEach 循环,因为 listItems 是空的。我想这是因为 ng-repeat 正在等待 scope.playlists 填充来自通过 $resource 对服务器的异步调用的数据。

临时修复

在指令定义中,我添加了一个布尔变量,用于检查元素属性中是否存在“add”:var adding = 'add' in attrs ? true : false;

然后在模板中,

a.list-group-item(ng-if='adding', ng-repeat='playlist in playlists', ng-click='addToPlaylist(playlist, track)')
a.list-group-item(ng-if='!adding', ng-repeat='playlist in playlists', ng-href='playlist/{{playlist._id }}')

虽然它工作正常,但它显然根本不是 DRY。帮助!

【问题讨论】:

  • 您的最终解决方案实际上并没有那么糟糕,尽管它似乎是重复的。

标签: javascript jquery angularjs jqlite


【解决方案1】:

更改您的点击处理程序,而不是删除属性。

$event 添加到参数列表并有条件地使用preventDefault()

<a ng-click='addToPlaylist($event,playlist)' ng-href='playlist'>CLICK ME</a>

在您的控制器中:

$scope.addToPlaylist = function(event,playlist) {
     if (!$scope.adding) return;
     //otherwise
     event.preventDefault();
     //do add operation
};

不添加时,函数返回并获取href。否则会阻止默认设置,点击处理程序会执行添加操作。

来自文档:

$事件

ngClickngFocus 这样的指令在该表达式的范围内公开了一个$event 对象。当 jQuery 存在时,该对象是 jQuery Event Object 的实例或类似的 jqLit​​e 对象。

-- AngularJS Developer Guide -- $event

【讨论】:

  • 非常感谢这个解决方案。它就像一个魅力,并以一种干燥的方式解决问题。谢谢!
  • 解决方案不仅干燥。 无需编写自定义指令即可解决您的问题。您可以使用内置指令实现您想要的。感谢您的支持。
【解决方案2】:

您尝试做事的方式可能不是最 Angularish(Angularist?Angularyist?)的方式。当您在这里尝试使用angular.element() 选择子元素时,您可以确保子元素准备就绪,如下所示:

link: function(scope, elm, attrs) {
  elm.ready(function() {
    var listItems = angular.element(element[0].getElementsByClassName('list-group-item')
    angular.forEach(listItems, function(item, index) {
      'add' in attrs ? item.removeAttr('href') : item.removeAttr('ng-click');
      listItems[index] = item;
    }
  });
}

但是,这不太可能适用于您的情况,例如 @charlietfl points out below。如果您想避免已有的解决方案(我认为这比您的第一次尝试更好),您将不得不完全重新实现您的代码。

我建议使用指令定义对象的require 属性定义一个附加指令communicates with its parent directive。新指令可以访问父指令的add 属性(父指令的控制器 中的this.add),并且可以对其进行编程以实现相应的行为。该解决方案的实施超出了此答案的范围。

更新:

我决定试一试实施。该示例被高度简化,但它完成了您正在尝试做的事情:根据传递给它的属性更改指令的模板。请参阅示例here

该示例使用 Angular 1 中的一个新功能:components。您可以阅读更多关于可注入模板和组件的信息here。本质上,组件允许您使用可以访问元素及其属性的函数来定义模板,如下所示:

app.component('playlistComponent', {

    // We can define out template as a function that returns a string:
    template: function($element, $attrs) {
      var action = 'add' in $attrs
        ? 'ng-click="$ctrl.addToPlaylist(playlist, track)"'
        : 'ng-href="playlist/{{playlist._id}}"';

      return '<a class="list-group-item" ng-repeat="playlist in playlists" ' +
        action + '></a>';
    },

    // Components always use controllers rather than scopes
    controller: ['playlistService', function(playlists) {
      this.playlists = playlists;

      this.addToPlaylist = function(playlist, track) {
        // Some logic
      };
    }]
  });

【讨论】:

  • 解释没有意义。在 dom 准备好之前,Angular 不会引导,并且除非元素存在,否则指令不能触发。对ready 工作方式的理解是不准确的。作为孩子重复元素是一种不同的情况
  • @charlietfl 您能否编辑答案以更正它或提供更好的答案?我可能措辞不准确,但我认为解决方案会奏效。我会尝试改写,但如果我仍然不正确,请随时编辑。
  • 整个问题看起来像“先有鸡”。首先循环遍历重复的 dom 节点根本没有意义
  • @charlietfl 好的,我同意。这就是我提出替代方案的原因,尽管这需要完全不同的实现。
  • 但是我不认为删除 ng-click 属性在它已经编译后仍然可以工作......不会删除元素上的事件侦听器
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-07
  • 2013-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-04
相关资源
最近更新 更多