wake-up-neo.com

Die Direktive isoliert den Geltungsbereich mit dem Geltungsbereich ng-repeat in AngularJS

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:

  • Wie kann ich verhindern, dass ng-repeat Einen Bereich erstellt, der prototypisch vom übergeordneten Bereich erbt, und stattdessen den Isolate-Bereich meiner Direktive verwenden?
  • Warum funktioniert die Prioritätsstufe 2000 In meiner Direktive nicht?
  • Kann man mit Batarang wissen, welche Art von Zielfernrohr verwendet wird?
95
Deepak Nulu

Okay, durch viele der obigen Kommentare habe ich die Verwirrung entdeckt. Zunächst einige Klarstellungen:

  • ngRepeat hat keinen Einfluss auf den von Ihnen gewählten Isolationsbereich
  • die an ngRepeat übergebenen Parameter zur Verwendung mit den Attributen Ihrer Direktive do verwenden einen prototypisch vererbten Gültigkeitsbereich
  • der Grund, warum Ihre Direktive nicht funktioniert, hat nichts mit dem isolierten Bereich zu tun

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.

enter image description here

Nachdem Sie auf das erste Element geklickt haben, sehen die Bereiche nun folgendermaßen aus:

enter image description here

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:

enter image description here


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:

enter image description here

Nach dem Klicken auf das erste Element:

enter image description here

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:

enter image description here

Bereiche nach dem Klicken auf das erste Element:

enter image description here

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.

203

Schauen Sie sich stattdessen die folgende Geige an, ohne direkt zu versuchen, die Beantwortung Ihrer Fragen zu vermeiden:

http://jsfiddle.net/dVPLM/

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.
  • Die Priorität in Direktiven ist nicht nur die Ausführungsreihenfolge - siehe: AngularJS: Wie ordnet der HTML-Compiler die Reihenfolge für das Kompilieren?
  • Wenn Sie in Batarang die Registerkarte Leistung aktivieren, können Sie die Ausdrücke für jeden Bereich anzeigen und überprüfen, ob diese Ihren Erwartungen entsprechen.
6
Alex Osborn