Directive template unique IDs for elements in AngularJS

AngularjsTemplatesAngularjs DirectiveUniqueidentifier

Angularjs Problem Overview


I have a directive that can be used multiple times on a page. In the template of this directive, I need to use IDs for an input-Element so I can "bind" a Label to it like so:

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

Now the problem is, as soon as my directive is included multiple times, the ID "item1" is not unique anymore and the label doesn't work correctly (it should check/uncheck the checkbox when clicked).

How is this problem fixed? Is there a way to assign a "namespace" or "prefix" for the template (like asp.net does with the ctl00...-Prefix)? Or do I have to include an angular-Expression in each id-Attribute which consists of the directive-ID from the Scope + a static ID. Something like:

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

Edit:

My Directive

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

My HTML

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

Angularjs Solutions


Solution 1 - Angularjs

HTML

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

Solution 2 - Angularjs

UPDATE

Angular 1.3 introduced a native lazy one-time binding. from the angular expression documentation:

> One-time binding > > An expression that starts with :: is considered a > one-time expression. One-time expressions will stop recalculating once > they are stable, which happens after the first digest if the > expression result is a non-undefined value (see value stabilization > algorithm below).

Native Solution:

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

Only bind once:
  • If you only need to bind a value once you should not use bindings ({{}} / ng-bind)
  • bindings are expensive because they use $watch. In your example, upon every $digest, angular dirty checks your IDs for changes but you only set them once.
  • Check this module: https://github.com/Pasvaz/bindonce

Solution:

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

Solution 3 - Angularjs

We add a BlockId parameter to the scope, because we use the id in our Selenium tests for example. There is still a chance of them not being unique, but we prefer to have complete control over them. Another advantage is that we can give the item a more descriptive id.

Directive JS

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

Directive HTML

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

Usage

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

Solution 4 - Angularjs

Apart from Ilan and BuriB's solutions (which are more generic, which is good) I found a solution to my specific problem because I needed IDs for the "for" Attribute of the label. Instead the following code can be used:

<label><input type="checkbox"/>open</label>

The following Stackoverflow-Post has helped:

https://stackoverflow.com/a/14729165/1288552

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNoRybView Question on Stackoverflow
Solution 1 - AngularjsBuriBView Answer on Stackoverflow
Solution 2 - AngularjsIlan FrumerView Answer on Stackoverflow
Solution 3 - AngularjsRafView Answer on Stackoverflow
Solution 4 - AngularjsNoRybView Answer on Stackoverflow