Soweit ich bemerkt habe, kompiliert Angular die Dinge nach dem Prinzip "Wer zuerst kommt, mahlt zuerst", was etwas schwierig ist. Ich habe eine Direktive erstellt, die einige Elemente umschließt, und ich möchte eine Link-Eigenschaft haben, die nach Inhalten sucht.

Für einen konkreten Anwendungsfall: Ich erstelle eine Eingabe-Label-Direktive, die im Inhalt nach der ersten Eingabe sucht und dem input ein zufällig generiertes id und dem ein for Attribut hinzufügt label

Hier ist der Code:

// Find the first element with the attribute ng-label-target or the first input and links a label to it
app.directive('ngLabel', function () {
  return {
    restrict: 'E',
    replace: true,
    transclude: true,
    scope: {
      label: '@',
    },
    template: '<span class="ng-label">' +
                '<label class="ng-label-text">{{label}}</label>' +
                '<span class="ng-label-content" ng-transclude></span>' +
              '</span>',
    link: function (scope, element, attrs) {
      scope.id = scope.id || 'random-id-' + Math.floor(Math.random() * 90000000);
      angular.element(element[0].querySelector('.ng-label-text')).
        attr({for:scope.id});

      var target = angular.element(element[0].querySelector('[ng-label-target]'));
      if (!target || target.length == 0) {
        target = angular.element(element[0].querySelectorAll('input,select,textarea')[0]);
      }
      target.attr({id:scope.id});
    },
  };
});

Beispiel Verwendung:

<ng-label label="Text:">
   <input type="text" ng-model="page.textColor" size="5" maxlength="7" placeholder="e.g. #000" required />
   <input ng-label-target type="color" ng-model="page.textColor" required />
</ng-label>

Dies allein wirkt wie ein Zauber.

Jetzt möchte ich jedoch mehrere Eingaben automatisch generieren und die Beschriftung auf die erste verweisen lassen. Das Problem ist, dass, wenn ich ein ng-repeat in meinem ng-label mache, ng-repeat Code generiert wird, nachdem meine Direktive verarbeitet wurde, sodass tatsächlich nichts gefunden wird.

Daher lautet meine Frage: Gibt es eine Möglichkeit, wie Sie Angular angeben können, um verschachtelte Direktiven von innen nach außen zu bewerten, anstatt umgekehrt?

Oder gibt es einen besseren Weg, dies zu tun, als ich es gerade tue?

Ich habe eine Geige gemacht, um die Reihenfolge zu veranschaulichen, in der Dinge bewertet werden. Sie sehen, dass die äußere Direktive einen kleineren oder gleichen Wert als ihr Inhalt hat (kann nicht niedriger als Mikrosekunden sein, daher musste ich einige Wiederholungen durchführen):

http://jsfiddle.net/YLM9P/

15
Stefan 8 Dez. 2013 im 22:40

4 Antworten

Beste Antwort

Aus den eckigen Dokumenten:

PRIORITÄT Wenn für ein einzelnes DOM-Element mehrere Direktiven definiert sind, muss manchmal die Reihenfolge angegeben werden, in der die Direktiven angewendet werden. Die Priorität wird verwendet, um die Anweisungen zu sortieren, bevor ihre Kompilierungsfunktionen aufgerufen werden. Priorität ist als Zahl definiert. Direktiven mit höherer numerischer Priorität werden zuerst kompiliert. Pre-Link-Funktionen werden ebenfalls in der Prioritätsreihenfolge ausgeführt, Post-Link-Funktionen jedoch in umgekehrter Reihenfolge. Die Reihenfolge der Anweisungen mit derselben Priorität ist undefiniert. Die Standardpriorität ist 0.

TERMINAL Wenn true festgelegt ist, ist die aktuelle Priorität der letzte Satz von Anweisungen, die ausgeführt werden (alle Anweisungen mit der aktuellen Priorität werden weiterhin ausgeführt, da die Ausführungsreihenfolge für dieselbe Priorität nicht definiert ist).

Ich glaube, dass Sie Ihr Problem lösen können, wenn Sie die Terminal-Eigenschaft auf true setzen.

app.directive('ngLabel', function () {
  return {
    restrict: 'E',
    replace: true,
    transclude: true,
    ....
    ....
    terminal: true
});
19
NicolasMoise 12 Dez. 2013 im 14:58

Dieses Problem kann, wie viele andere in Angular, durch die Erstellung einer zusätzlichen Direktive gelöst werden:

app.directive('myLabelTarget', function() {
 return {
   require: '^myLabel',
   link: function(scope, element, attrs, myLabelCtrl) {
     myLabelCtrl.doIfFirst(function(id) {
       attrs.$set('id', id);  
     });
   }
 };
});

Mit dem Attribut require können Sie auf den Controller der myLabel-Direktive weiter oben im DOM zugreifen.

Ein Beispiel finden Sie in diesem plnkr.

1
Pieter Herroelen 16 Dez. 2013 im 16:06

Sie können versuchen, einen MutationObserver zu registrieren, der auf Elemente wartet, die dem DOM hinzugefügt wurden. Aber das könnte ein wenig Aufwand für Ihr Problem sein - Sie entscheiden;)

var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    // you get notified about added DOM elements here
    // check mutation.type if it was an insertion
    // then you handle the first node specifically.
    console.log(mutation.target.nodeName, mutation.type, mutation);
  });
});

var config = {childList: true, attributes: false, characterData: false, subtree: false, attributeOldValue: false, characterDataOldValue: false};
observer.observe(element[0], config);
Demo              http://plnkr.co/h6kTtq
Documentation     https://developer.mozilla.org/en/docs/Web/API/MutationObserver
Browser support   http://caniuse.com/mutationobserver
0
angabriel 23 Dez. 2013 im 14:47

Ich denke, Sie können Ihren Code erheblich vereinfachen und Ihre querySelectorAll (nicht besonders eckig) eliminieren, indem Sie dem übergeordneten Element einen Funktionsausdruck hinzufügen (möglicherweise einen Parameter übergeben) und diese Funktion dann von den untergeordneten Elementen innerhalb des ng aufrufen -wiederholen. Die Funktion würde einfach einen Wert festlegen, den Sie verwenden würden, und sobald er festgelegt ist, wissen Sie, dass Sie den ersten haben und der Rest sollte ignoriert werden. Ich könnte etwas genauer sein, wenn Sie zeigen würden, wie der endgültige HTML-Code mit Ihren ng-repeat-Eingabefeldern aussehen sollte.

0
Darryl 9 Dez. 2013 im 18:33