14

Soweit ich festgestellt habe Angular kompiliert Dinge auf einer first-come, first-serve-Basis, die eine Art schwierig ist. Ich habe eine Direktive gemacht, die einige Elemente umschließt und ich möchte eine Link-Eigenschaft haben, die nach Inhalten im Inhalt sucht.AngularJS Reihenfolge ändern, welche Direktiven zusammengestellt werden

Für einen konkreten Anwendungsfall: Ich mache eine Eingabe-Label-Richtlinie, die für den ersten Eingang in den Inhalt aussieht und fügt eine zufällig generierte id zum input und ein for Attribut auf den 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}); 
    }, 
    }; 
}); 

Verwendungsbeispiel:

<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> 

Das allein wirkt wie ein Zauber.

Aber jetzt möchte ich automatisch mehrere Eingaben generieren und den Label-Punkt auf den ersten haben. Das Problem ist, dass wenn ich eine ng-repeat innerhalb meiner ng-label dann ng-repeat Code wird generiert, nachdem meine Anweisung wurde verarbeitet, so dass nichts tatsächlich gefunden wird.

Daher ist meine Frage: Gibt es eine Möglichkeit, in der Sie Winkel angeben können, um verschachtelte Direktiven von innen nach außen auszuwerten anstatt umgekehrt?

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

Ich machte eine Fiedel, um die Reihenfolge zu illustrieren, in der Zeug ausgewertet wird. Sie sehen, dass die äußere-Richtlinie einen kleineren oder gleichen Wert hat, als es Inhalt ist (nicht niedriger als Mikrosekunden gehen kann, damit ich ein paar Wiederholungen zu tun hatte):

http://jsfiddle.net/YLM9P/

+0

sicher sieht viel komplizierter als nötig sein ... Demo zur Verfügung gestellt hat zu viel Rekursion, bieten Demo mit Ihrer Anweisung und Markup. Sie können finden, dass Plunker einfacher ist, eckige Demos mit als Geige zu erstellen – charlietfl

+0

Warum muss das 'id' zufällig sein? Kannst du nicht einfach 'id =" name _ {{$ index}} "' 'und das selbe für das Label benutzen? –

+0

Ich möchte nicht die ID angeben, da ich es im Allgemeinen nicht brauche, deshalb ist es zufällig, wenn es nicht angegeben wird – Stefan

Antwort

18

aus dem Winkel docs:

PRIORITY Wenn mehrere Richtlinien auf einem einzelnen DOM-Elemente definiert ist, ist es manchmal notwendig, die Reihenfolge, in der die Richtlinien angewandt werden, zu spezifizieren. Die Priorität wird verwendet, um die Anweisungen zu sortieren, bevor ihre Kompilierfunktionen aufgerufen werden. Priorität ist als Nummer definiert. Richtlinien mit höherer numerischer Priorität werden zuerst kompiliert. Pre-Link-Funktionen werden ebenfalls in der Reihenfolge der Priorität ausgeführt, Post-Link-Funktionen werden jedoch in umgekehrter Reihenfolge ausgeführt. Die Reihenfolge der Anweisungen mit derselben Priorität ist nicht definiert. Die Standardpriorität ist 0.

TERMINAL Wenn dann wird die aktuelle Priorität der letzte Satz von Richtlinien auf true gesetzt, die (alle Richtlinien in der aktuellen Priorität ausführt werden immer noch als die Reihenfolge der Ausführung auf gleiche Priorität auszuführen ist nicht definiert).

Also für Ihr Problem, ich glaube, die Terminal-Eigenschaft auf True setzen wird Ihr Problem lösen.

app.directive('ngLabel', function() { 
    return { 
    restrict: 'E', 
    replace: true, 
    transclude: true, 
    .... 
    .... 
    terminal: true 
}); 
+1

Es scheint, dass Direktiven mit niedrigerer Priorität zuerst ausgeführt werden. – Ghigo

0

Ich glaube, Sie erheblich vereinfachen können Ihr Code und eliminieren Sie Ihre QuerySelectorAll (nicht schrecklich Angular-Esque) durch Hinzufügen eines Funktionsausdrucks auf das Elternelement (möglicherweise übergibt in einem Parameter), und rufen Sie dann diese Funktion aus dem Child-Element (s) innerhalb der ng-Wiederholung. Die Funktion würde einfach einen Wert setzen, den Sie verwenden würden, und sobald sie gesetzt 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 soll.

0

können Sie versuchen, eine MutationObserver zu registrieren, die für Elemente lauscht den DOM hinzugefügt. Aber dies könnte ein wenig Aufwand, um 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 
1

Dieses Problem, wie viele andere in Angular, kann durch die Schaffung einer zusätzlichen Richtlinie 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 im DOM zugreifen.

Siehe dieses plnkr für ein funktionierendes Beispiel.