Eindeutige IDs für Direktivenvorlagen für Elemente in AngularJS

70

Ich habe eine Direktive, die auf einer Seite mehrfach verwendet werden kann. In der Vorlage dieser Direktive muss ich IDs für ein Eingabeelement verwenden, damit ich ein Label wie folgt daran "binden" kann:

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

Jetzt ist das Problem, sobald meine Direktive mehrmals enthalten ist, die ID "item1" nicht mehr eindeutig ist und die Beschriftung nicht richtig funktioniert (sie sollte das Kontrollkästchen aktivieren / deaktivieren, wenn sie angeklickt wird).

Wie wird dieses Problem behoben? Gibt es eine Möglichkeit, der Vorlage einen "Namespace" oder ein "Präfix" zuzuweisen (wie es asp.net mit dem Präfix ctl00 ...- tut)? Oder muss ich in jedes ID-Attribut einen Winkelausdruck einfügen, der aus der Direktiven-ID aus dem Bereich + einer statischen ID besteht? Etwas wie:

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

Bearbeiten:

Meine Richtlinie

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

Mein HTML

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

Antworten:

93

HTML

    <div class="myDirective">
        <input type="checkbox" id="myItem_{{$id}}" />
        <label for="myItem_{{$id}}">open myItem_{{$id}}</label>
    </div>
BuriB
quelle
10
Elegante Lösung. $ id ist die eindeutige Bereichs-ID, die von $ rootScope für jeden untergeordneten Bereich bereitgestellt wird. Offensichtlich kann dies für jede Ansicht verwendet werden, die einen anderen Bereich hat, was meistens der Fall ist.
Sai Dubbaka
7
Sollte dies auch den Trick {{:: expression}} aus der Antwort von @ llan kombinieren, um zu vermeiden, dass mehr Uhren erstellt werden?
Pinguin359
7
@ penguin359 yeap, du kannst auch einmal binden mit {{::$id}}. Zu dem Zeitpunkt, als mein Kommentar abgegeben wurde, war 1.3 noch nicht erschienen.
BuriB
Interessant, dass es nicht möglich ist, verschachtelte Elemente mit dem dynamischen Selektor in der linkMethode der Direktive abzufragen : element.find(`#myItem_ ${scope.$id}`), weil die Vorlage ihre dynamischen Werte noch nicht kompiliert hat…
Nik Sumeiko
Gibt es eine Lösung dafür? (dh Niks Kommentar). Was ist, wenn Sie innerhalb der Verknüpfungsfunktion die Elemente mit der dynamischen ID ändern müssen? Gibt es keine Möglichkeit, dies zu erreichen?
Geoidesic
51

AKTUALISIEREN

Angular 1.3 führte eine native faule einmalige Bindung ein. aus der Dokumentation des Winkelausdrucks :

Einmalige Bindung

Ein Ausdruck, der mit :: beginnt, wird als einmaliger Ausdruck betrachtet. Einmalige Ausdrücke werden nicht mehr neu berechnet, sobald sie stabil sind. Dies geschieht nach dem ersten Digest, wenn das Ausdrucksergebnis ein nicht undefinierter Wert ist (siehe Wertstabilisierungsalgorithmus unten).

Native Lösung :

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

Nur einmal binden:

  • Wenn Sie einen Wert nur einmal binden müssen, sollten Sie keine Bindungen verwenden ({{}} / ng-bind).
  • Bindungen sind teuer, weil sie $ watch verwenden. In Ihrem Beispiel überprüft Angular Dirty Ihre IDs bei jedem $ Digest auf Änderungen, aber Sie legen sie nur einmal fest.
  • Überprüfen Sie dieses Modul: https://github.com/Pasvaz/bindonce

Lösung:

.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);
        }
    }
})
Ilan Frumer
quelle
3
Erwähnt werden sollte, dass Sie seit 1.3 (zu diesem Zeitpunkt noch in RC) einmal mit der Notation binden können {{:yourExpression}}.
J_A_X
Die einmalige Bindungsfunktion ist jetzt hier dokumentiert: docs.angularjs.org/guide/expression#one-time-binding
NoRyb
Wir können diese Technik auch verwenden, indem wir dem folgenden Ansatzlink folgen: function (scope, elem, attrs) {var item = 'item' + uniqueId ++; elem.find ('input'). attr ('id', item); var element = document.getElementById (item); Jetzt im Element erhalten Sie dom Objekt und Sie können alles tun :). Es ist wirklich eine gute Technik, um dom dynamisch zu manipulieren, wobei die Element-ID dynamisch festgelegt wird!}
Mohammad tanvirul islam
1
Mohammed, dein Beispiel ist nicht klar. Könnten Sie bitte eine Antwort hinzufügen, die klarer zeigt, was Sie sagen?
Geoidesic
@Mohammadtanvirulislam DOM-Manipulation ist im Allgemeinen eine schlechte Idee. Warum sollten Sie das tun wollen, was Sie gesagt haben? Und es fügt der ursprünglichen Frage / Antwort nicht wirklich etwas hinzu - es ist ein ganz anderes Thema.
NoRyb
2

Wir fügen dem Bereich einen BlockId-Parameter hinzu, da wir die ID beispielsweise in unseren Selenium-Tests verwenden. Es besteht immer noch die Möglichkeit, dass sie nicht einzigartig sind, aber wir bevorzugen die vollständige Kontrolle über sie. Ein weiterer Vorteil ist, dass wir dem Artikel eine aussagekräftigere ID geben können.

Richtlinie JS

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

Direktive HTML

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

Verwendung

<my-directive block-id="descriptiveName"></my-directive>
Raf
quelle
1

Abgesehen von den Lösungen von Ilan und BuriB (die allgemeiner sind, was gut ist), habe ich eine Lösung für mein spezifisches Problem gefunden, da ich IDs für das Attribut "for" des Etiketts benötigte. Stattdessen kann der folgende Code verwendet werden:

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

Der folgende Stackoverflow-Post hat geholfen:

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

NoRyb
quelle
1
Wenn Sie jedoch Bootstrap verwenden, verwenden Sie diesen Ansatz nicht. Weil Bootstrap nicht zulässt, dass Eingaben in Beschriftungen eingebettet werden.
Sai Dubbaka