Ich habe eine Direktive mit einem isolierten Gültigkeitsbereich (damit ich die Direktive an anderer Stelle wiederverwenden kann), und wenn ich diese Direktive mit einem ng-repeat
Verwende, funktioniert sie nicht.
Ich habe die gesamte Dokumentation und die Antworten zum Stapelüberlauf zu diesem Thema gelesen und die Probleme verstanden. Ich glaube, ich habe alle üblichen Fallstricke vermieden.
Ich verstehe also, dass mein Code aufgrund des durch die Direktive ng-repeat
Erzeugten Gültigkeitsbereichs fehlschlägt. Meine eigene Direktive erstellt einen Isolationsbereich und führt eine bidirektionale Datenbindung an ein Objekt im übergeordneten Bereich durch. Meine Direktive weist dieser gebundenen Variablen einen neuen Objektwert zu und dies funktioniert perfekt, wenn meine Direktive ohne ng-repeat
Verwendet wird (die übergeordnete Variable wird korrekt aktualisiert). Mit ng-repeat
Erstellt die Zuweisung jedoch eine neue Variable im Bereich ng-repeat
, Und die übergeordnete Variable sieht die Änderung nicht. All dies ist wie erwartet, basierend auf dem, was ich gelesen habe.
Ich habe auch gelesen, dass bei mehreren Anweisungen für ein bestimmtes Element nur ein Bereich erstellt wird. Und dass in jeder Direktive ein priority
gesetzt werden kann, um die Reihenfolge zu definieren, in der die Direktiven angewendet werden; Die Direktiven werden nach Priorität sortiert und ihre Kompilierungsfunktionen aufgerufen (suchen Sie nach der Word-Priorität unter http://docs.angularjs.org/guide/directive ).
Ich hatte gehofft, dass ich mithilfe von "priority" sicherstellen kann, dass meine Direktive zuerst ausgeführt wird und schließlich einen Isolatbereich erstellt. Wenn ng-repeat
Ausgeführt wird, wird der Isolatbereich erneut verwendet, anstatt einen prototypischen Bereich zu erstellen erbt vom übergeordneten Bereich. In der Dokumentation ng-repeat
Wird angegeben, dass diese Direktive auf der Prioritätsebene 1000
Ausgeführt wird. Es ist nicht klar, ob 1
Eine höhere oder eine niedrigere Priorität hat. Als ich in meiner Direktive die Prioritätsstufe 1
Verwendet habe, hat das keinen Unterschied gemacht, also habe ich versucht, 2000
. Aber das macht die Sache noch schlimmer: Meine bidirektionalen Bindungen werden zu undefined
und meine Direktive zeigt nichts an.
Ich habe eine Geige erstellt , um mein Problem zu zeigen . Ich habe die Einstellung priority
in meiner Direktive auskommentiert. Ich habe eine Liste von Namensobjekten und eine Direktive mit dem Namen name-row
, Die die Felder für den Vor- und Nachnamen im Namensobjekt anzeigt. Wenn ein angezeigter Name angeklickt wird, soll er eine selected
-Variable im Hauptbereich festlegen. Das Array mit den Namen und die Variable selected
werden mit bidirektionaler Datenbindung an die Direktive name-row
Übergeben.
Ich weiß, wie dies funktioniert, indem ich Funktionen im Hauptbereich aufrufe. Ich weiß auch, dass, wenn sich selected
in einem anderen Objekt befindet und ich mich an das äußere Objekt binde, die Dinge funktionieren würden. Aber diese Lösungen interessieren mich im Moment nicht.
Stattdessen sind die Fragen, die ich habe:
ng-repeat
Einen Bereich erstellt, der prototypisch vom übergeordneten Bereich erbt, und stattdessen den Isolate-Bereich meiner Direktive verwenden?2000
In meiner Direktive nicht?Okay, durch viele der obigen Kommentare habe ich die Verwirrung entdeckt. Zunächst einige Klarstellungen:
Hier ist ein Beispiel für denselben Code, bei dem die Direktive entfernt wurde:
<li ng-repeat="name in names"
ng-class="{ active: $index == selected }"
ng-click="selected = $index">
{{$index}}: {{name.first}} {{name.last}}
</li>
Hier ist eine JSFiddle Demonstration, dass es nicht funktioniert. Sie erhalten genau die gleichen Ergebnisse wie in Ihrer Direktive.
Warum funktioniert es nicht? Weil Bereiche in AngularJS prototypische Vererbung verwenden. Der Wert selected
in Ihrem übergeordneten Bereich ist ein Grundelement . In JavaScript bedeutet dies, dass es überschrieben wird, wenn ein untergeordnetes Element denselben Wert festlegt. Es gibt eine goldene Regel in AngularJS-Bereichen: Modellwerte sollten immer einen .
Enthalten. Das heißt, sie sollten niemals primitiv sein. Siehe this SO answer für weitere Informationen.
Hier ist ein Bild davon, wie die Bereiche anfangs aussehen.
Nachdem Sie auf das erste Element geklickt haben, sehen die Bereiche nun folgendermaßen aus:
Beachten Sie, dass im Bereich ngRepeat eine neue Eigenschaft selected
erstellt wurde. Der Controller-Bereich 003 wurde nicht geändert.
Sie können wahrscheinlich erraten, was passiert, wenn wir auf den zweiten Punkt klicken:
Ihr Problem wird also eigentlich gar nicht durch ngRepeat verursacht - es wird durch das Brechen einer goldenen Regel in AngularJS verursacht. Sie können dies beheben, indem Sie einfach eine Objekteigenschaft verwenden:
$scope.state = { selected: undefined };
<li ng-repeat="name in names"
ng-class="{ active: $index == state.selected }"
ng-click="state.selected = $index">
{{$index}}: {{name.first}} {{name.last}}
</li>
Hier ist eine zweite JSFiddle , die zeigt, dass dies auch funktioniert.
So sehen die Bereiche zunächst aus:
Nach dem Klicken auf das erste Element:
Hier ist der Steuerungsumfang wie gewünscht betroffen.
Um zu beweisen, dass dies auch mit Ihrer Direktive mit einem isolierten Gültigkeitsbereich noch funktioniert (da dies wiederum nichts mit Ihrem Problem zu tun hat), ist hier ein JSFiddle . Auch dafür muss die Sicht reflektiert werden das Objekt. Sie werden feststellen, dass die einzige notwendige Änderung darin bestand, ein Objekt anstelle eines Grundelements zu verwenden.
Bereiche anfangs:
Bereiche nach dem Klicken auf das erste Element:
Fazit: Auch hier liegt Ihr Problem nicht im isolierten Bereich und nicht in der Funktionsweise von ngRepeat. Ihr Problem ist, dass Sie eine Regel brechen, von der bekannt ist, dass sie genau zu diesem Problem führt. Modelle in AngularJS sollten immer einen .
Haben.
Schauen Sie sich stattdessen die folgende Geige an, ohne direkt zu versuchen, die Beantwortung Ihrer Fragen zu vermeiden:
Der entscheidende Punkt ist, dass Sie Ihre Direktive so strukturieren können, dass sie mit ng-repeat
Arbeitet, anstatt zu versuchen, sie zu überschreiben, anstatt zu versuchen, das herkömmliche Verhalten von Angular zu bekämpfen und zu ändern.
In Ihrer Vorlage:
<name-row
in-names-list="names"
io-selected="selected">
</name-row>
In Ihrer Direktive:
template:
' <ul>' +
' <li ng-repeat="name in inNamesList" ng-class="activeClass($index)" >' +
' <a ng-click="setSelected($index)">' +
' {{$index}} - {{name.first}} {{name.last}}' +
' </a>' +
' </li>' +
' </ul>'
Als Antwort auf Ihre Fragen:
ng-repeat
Erstellt einen Bereich, Sie sollten wirklich nicht versuchen, dies zu ändern.