AngularJS中为元素指令模板分配唯一ID的方法

70

我有一个指令可以在页面上使用多次。在这个指令的模板中,我需要为一个输入元素使用ID,以便我可以将标签与它“绑定”,例如:

<input type="checkbox" id="item1" /><label for="item1">open</label>

现在的问题是,当我的指令被多次包含时,ID“item1”不再是唯一的,标签就无法正常工作了(它应该在单击时选中/取消选中复选框)。

如何解决这个问题?是否有一种方法为模板分配“命名空间”或“前缀”(就像asp.net使用ctl00...-前缀那样)?或者我必须在每个ID属性中包含一个Angular表达式,其中包含来自Scope的指令ID + 静态ID。类似于:

<input type="checkbox" id="{{directiveID}} + 'item1'" /><label for="{{directiveID}} + 'item1'">open</label>

编辑:

我的指令

module.directive('myDirective', function () {
    return {
        restrict: 'E',
        scope: true, 
        templateUrl: 'partials/_myDirective.html',
        controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
            ...
        } //controller
    };
}]);

我的HTML

<div class="myDirective">
  <input type="checkbox" id="item1" /><label for="item1">open</label>
</div>
4个回答

94

HTML

    <div class="myDirective">
        <input type="checkbox" id="myItem_{{$id}}" />
        <label for="myItem_{{$id}}">open myItem_{{$id}}</label>
    </div>

10
优雅的解决方案。$id是由$rootScope为每个子作用域提供的唯一作用域标识符。显然,这可用于任何具有不同作用域的视图,这通常是情况。 - Sai Dubbaka
7
是否应该结合@llan答案中的{{::expression}}技巧,以避免创建更多的监视器? - penguin359
7
是的,你也可以使用{{::$id}}进行一次性绑定。在我发表评论的时候,1.3版本还没有发布。 - BuriB
在我的情况下,标签和文本字段的ID是不同的,尽管两者都嵌套在同一个父div中。这只有在父指令时才有效吗? - user1438038
这个有没有 Angular2/8 的类似物? - azulBonnet
显示剩余3条评论

51

更新

Angular 1.3引入了本地的惰性单次绑定。从angular表达式文档中可知:

一次性绑定

一个以 :: 开头的表达式被认为是一次性表达式。一次性表达式在稳定后将停止重新计算,这种情况发生在第一次脏检查(digest)之后,如果表达式结果是一个 非 undefined 值 (请参阅下面的值稳定化算法)。

本地解决方案:

.directive('myDirective', function() {

    var uniqueId = 1;
    return {
        restrict: 'E',
        scope: true,
        template: '<input type="checkbox" id="{{::uniqueId}}"/>' +
                  '<label for="{{::uniqueId}}">open</label>',
        link: function(scope, elem, attrs) {
            scope.uniqueId = 'item' + uniqueId++;
        }
    }
})

仅绑定一次:

  • 如果您只需要将值绑定一次,不应使用绑定 ({{}} / ng-bind)
  • 绑定使用 $watch,因此很昂贵。 在您的示例中,每次 $digest 时,Angular 都会检查 ID 是否发生更改,但您只设置它们一次。
  • 请查看此模块:https://github.com/Pasvaz/bindonce

解决方案:

.directive('myDirective', function() {

    var uniqueId = 1;
    return {
        restrict: 'E',
        scope: true,
        template: '<input type="checkbox"/><label>open</label>',
        link: function(scope, elem, attrs) {
            var item = 'item' + uniqueId++;
            elem.find('input').attr('id' , item);
            elem.find('label').attr('for', item);
        }
    }
})

3
需要说明的是,从1.3版本开始(当时仍处于RC阶段),您可以使用符号{{:yourExpression}}只绑定一次。 - J_A_X
一次性绑定功能现在已在此处记录:https://docs.angularjs.org/guide/expression#one-time-binding - NoRyb
我们也可以通过以下方法使用这种技术: link: function(scope, elem, attrs) { var item = 'item' + uniqueId++; elem.find('input').attr('id' , item); var element=document.getElementById(item); 现在你可以在element中获取dom对象并进行任何操作 :). 这是一种非常好的动态操作dom元素的技术,其中元素id是动态设置的! - Mohammad tanvirul islam
1
Mohammed,你的例子不够清晰。请你能否添加一个更明确展示你意思的答案? - geoidesic
@Mohammadtanvirulislam,DOM 操作通常是个不好的主意。你为什么要做你所说的事情?而且这并没有对原始问题/答案增加什么 - 这是一个完全不同的话题。 - NoRyb

2
我们在作用域中添加了一个BlockId参数,因为我们在Selenium测试中使用该ID。虽然仍有可能不唯一,但我们更喜欢完全控制它们。另一个优点是我们可以给该项一个更具描述性的ID。 指令JS
module.directive('myDirective', function () {
    return {
        restrict: 'E',
        scope: {
            blockId: '@'
        }, 
        templateUrl: 'partials/_myDirective.html',
        controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
            ...
        } //controller
    };
}]);

指令HTML

<div class="myDirective">
  <input type="checkbox" id="{{::blockId}}_item1" /><label for="{{::blockId}}_item1">open</label>
</div>

使用方法

<my-directive block-id="descriptiveName"></my-directive>

1
除了Ilan和BuriB的解决方案(这些方案更通用,这很好),我找到了一个解决我的特定问题的解决方案,因为我需要为标签的“for”属性获取ID。可以使用以下代码:
<label><input type="checkbox"/>open</label>

以下 Stackoverflow 帖子已经帮助了:

https://dev59.com/0nE85IYBdhLWcg3wl0nF#14729165


1
但是,如果您正在使用Bootstrap,则不使用该方法。因为Bootstrap不允许将输入嵌入标签中。 - Sai Dubbaka

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