简体   繁体   English

AngularJs指令:如何在ng-repeat元素上动态设置属性

[英]AngularJs Directive: How to dynamically set attributes on ng-repeat element

I am relatively new to AngularJS. 我对AngularJS比较陌生。 While venturing into directive creation, I can across this problem: How to dynamically add / remove attributes on the children of the directive's element when these children are dynamically added with 'ng-repeat'? 在尝试创建指令时,我可以解决这个问题: 当使用“ ng-repeat”动态添加子元素时,如何动态添加/删除指令元素的子元素上的属性?

First, I thought of this solution: 首先,我想到了这个解决方案:

template 模板

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

*directive *指示

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;
  }
... 

Result 结果

It turns out, my code never enters this angular.forEach loop because listItems is empty. 事实证明,因为listItems为空,所以我的代码从不进入这个angular.forEach循环。 I suppose it's because the ng-repeat is waiting for the scope.playlists to populate with the data from a async call to a server via $resource. 我想这是因为ng-repeat正在等待scope.playlists填充通过$ resource从异步调用到服务器的数据。

temporary fix 临时修复

in the directive definition, I added a boolean variable that checks for the presence of 'add' in the element's attributes: var adding = 'add' in attrs ? true : false; 在指令定义中,我添加了一个布尔变量,该变量检查元素的属性中是否存在“ add”: var adding = 'add' in attrs ? true : false; var adding = 'add' in attrs ? true : false;

And then in the template, 然后在模板中

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

While it works fine, it is obviously not DRY at all. 虽然工作正常,但显然根本不是干的。 HELP! 救命!

The way that you are trying to do things may not be the most Angularish (Angularist? Angularyist?) way. 您尝试做事的方式可能不是大多数Angularish(Angularist?Angularyist?)的方式。 When using angular.element() to select child elements as you are trying to do here, you can make sure the child elements are ready as follows: 当您尝试在此处使用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;
    }
  });
}

However, this is unlikely to work in your situation, as @charlietfl points out below . 但是,这不太可能在您的情况下起作用,因为@charlietfl在下面指出 If you want to avoid the solution you already have (which I think is better than your first attempt), you will have to reimplement your code altogether. 如果要避免使用已有的解决方案(我认为这比第一次尝试要好),则必须完全重新实现代码。

I would suggest defining an additional directive that communicates with its parent directive using the require property of the directive definition object. 我建议使用指令定义对象的require属性定义一个与其父指令通信的附加指令。 The new directive would have access to an add property of the parent ( this.add in the parent directive's controller ) and could be programmed to behave accordingly. 新指令可以访问父指令的add属性(在父指令的控制器中为this.add ),并且可以进行编程以使其行为相应。 The implementation of that solution is beyond the scope of this answer. 该解决方案的实现超出了此答案的范围。

Update: 更新:

I decided to give the implementation something of a shot. 我决定尝试一下实现。 The example is highly simplified, but it does what you are trying to do: alter the template of a directive based on the attributed passed to it. 该示例已高度简化,但是可以完成您要尝试的操作:根据传递给它的属性更改指令的模板。 See the example here . 请参阅此处的示例。

The example uses a new feature in Angular 1: components . 该示例在Angular 1: components中使用了新功能。 You can read more about injectable templates and components here . 您可以在此处阅读有关可注射模板和组件的更多信息。 Essentially, components allow you to define templates using a function with access to your element and its attributes, like so: 本质上,组件使您可以使用可访问元素及其属性的函数来定义模板,如下所示:

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

Instead of removing attributes, change your click handler. 而不是删除属性,而是更改您的点击处理程序。

Add $event to the list of arguments and conditionally use preventDefault() . $event添加到参数列表中,并有条件地使用preventDefault()

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

In your controller: 在您的控制器中:

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

When not adding, the function returns and the href is fetched. 不添加时,函数返回并获取href。 Otherwise the default is prevented and the click handler does the add operation. 否则,将阻止默认设置,并且单击处理程序将执行添加操作。

From the Docs: 从文档中:

$event $事件

Directives like ngClick and ngFocus expose a $event object within the scope of that expression. ngClickngFocus等指令在该表达式的范围内公开了$event对象。 The object is an instance of a jQuery Event Object when jQuery is present or a similar jqLite object. 当存在jQuery或类似的jqLit​​e对象时,该对象是jQuery事件对象的实例。

-- AngularJS Developer Guide -- $event -AngularJS开发人员指南-$ event

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM