AngularJS指令模板中访问DOM元素

68

在指令模板中,有没有更“Angular”的方法来选择DOM元素?例如,假设你有这个指令:

app.directive("myDirective", function() {
    return {
        template: '<div><ul><li ng-repeat="item in items"></ul></div>',
        link: function(scope, element, attrs) {
            var list = element.find("ul");
        }
    }
});
我使用了jQuery风格的选择器来获取在模板中呈现的DOM <ul> 元素。有更好的方法吗?

我使用了jQuery风格的选择器来获取在模板中呈现的DOM <ul> 元素。有更好的方法吗?


2
在状态中,我认为没有比这更好的方法了。但是选择一个元素可能有更好的选择。你的目标是什么,确切地说? - Blackhole
1
没有特别的事情。这只是我经常遇到的一个普遍问题。出于某种原因,我需要访问指令模板内部的DOM元素,但使用jQuery样式选择器似乎不太合适。 - Dustin
1
@Dustin,就像Blackhole所提到的,在90%的指令中,您不需要通过选择器访问DOM元素,这也是Angular的JQueryLite支持非常少量和基本选择器的原因。如果您能告诉我们您试图实现什么,那么有人会建议更好的方法来完成它。 - ganaraj
10
ngRepeat完成后如何将CSS样式应用于列表?你不能使用ng-class/ng-style和ng-repeat来实现吗?质疑您的问题的原因是,“选择DOM元素的Angular方式”的前提本身似乎是错误的,因为在大多数情况下,Angular方式并不是选择DOM元素。 - ganaraj
一个使用案例是嵌套列表,您想显示和隐藏父级的子项。或者,当您想单击一个元素并在标记中分开时隐藏第二个元素。ng-show - Kevin Suttle
显示剩余3条评论
3个回答

48

我认为选择元素没有更"angular way"的方法。例如,看看他们在这个旧文档页面的最后一个示例中是如何实现这个目标的:

{
     template: '<div>' +
    '<div class="title">{{title}}</div>' +
    '<div class="body" ng-transclude></div>' +
    '</div>',

    link: function(scope, element, attrs) {
        // Title element
        var title = angular.element(element.children()[0]),
        // ...
    }
}

1
这是正确的,但我认为即使在那个例子中,也有更加 Angular 的方法来完成相同的事情。模板中可以使用 ng-click 指令代替 title.bind('click'...) - Dustin
@DenisLins 很久以前是这样的情况。但是另一个答案提供了一个完全正确的解决方案,即使它的重量限制了它在某些特定场合的使用。 - Blackhole
1
我实际上尝试了这两个答案,它们都能正常工作。但这个更简单,而且不需要一个新的指令来完成。我是通过谷歌搜索得到这个问题的标题,我认为这个答案适用于像我这样的大多数情况。无论如何,如果它们都能正常工作,让另一个被接受也没有太多的坏处。 - Denis Lins

42
您可以编写一个指令来实现此功能,该指令仅使用具有属性赋值名称的(jqLite)元素将其分配给作用域。
以下是该指令内容:
app.directive("ngScopeElement", function () {
  var directiveDefinitionObject = {

    restrict: "A",

    compile: function compile(tElement, tAttrs, transclude) {
      return {
          pre: function preLink(scope, iElement, iAttrs, controller) {
            scope[iAttrs.ngScopeElement] = iElement;
          }
        };
    }
  };

  return directiveDefinitionObject;
});

使用方法:

app.directive("myDirective", function() {
    return {
        template: '<div><ul ng-scope-element="list"><li ng-repeat="item in items"></ul></div>',
        link: function(scope, element, attrs) {
            scope.list[0] // scope.list is the jqlite element, 
                          // scope.list[0] is the native dom element
        }
    }
});

一些注意事项:

  • 由于嵌套指令的编译和链接顺序, 您只能从myDirective的postLink函数中访问scope.list,这很可能是您正在使用的方式
  • ngScopeElement使用preLink函数,因此在具有ng-scope-element的元素内嵌指令已经可以访问scope.list
  • 不确定这样的性能会表现如何

2
我喜欢这个想法。更加简洁……不知道性能方面会有什么影响。 - Dustin
4
这是一个非常优雅的解决方案。我一定会在我的当前项目中实施它。 - Dan
3
如果 myDirective 声明了它自己的作用域,就需要使用 scope.$parent.list[0] 来获取那个元素。 - x1a0
2
如果您想访问ngIf内部的元素,这种方法可能行不通,因为它们很可能不存在。 - NoOne
错误:[$parse:isecdom] 在 Angular 表达式中引用 DOM 节点是不允许的!表达式:attachAttr($td, i) 可能会出现此问题。 - Stalin Gino

5
这个回答可能有点晚了,但我最近也遇到了类似的需求。根据问题中 @ganaraj 的评论,我需要的一个用例是,通过指令属性传递类名,以添加到模板中的 ng-repeat li 标签。
例如,可以像这样使用指令:
<my-directive class2add="special-class" />

以及获得以下html代码:

<div>
    <ul>
       <li class="special-class">Item 1</li>
       <li class="special-class">Item 2</li>
    </ul>
</div>

采用templateUrl的方式,可以在这里找到解决方案:

app.directive("myDirective", function() {
    return {
        template: function(element, attrs){
            return '<div><ul><li ng-repeat="item in items" class="'+attrs.class2add+'"></ul></div>';
        },
        link: function(scope, element, attrs) {
            var list = element.find("ul");
        }
    }
});

我刚刚成功地使用AngularJS 1.4.9进行了尝试。

希望这能有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接