Variablen-Scope in verschachtelten Generates¶
Wenn Du <variable>-Deklarationen von einem äußeren <generate> in ein verschachteltes <generate> verschiebst, sieht das XML oft noch fast gleich aus, aber der Auswertungskontext ändert sich. Diese Seite erklärt, was die Runtime tatsächlich macht, warum Ausdrücke wie voszData.data[index] plötzlich fehlschlagen oder den falschen Frame lesen und welche Präfixe stabil sind.
Kurzfassung¶
- Jede Iteration eines verschachtelten
<generate>läuft in einem eigenen Kontext-Frame. - In verschachtelten Frames ist
this.*der stabile Weg, um Variablen aus dem aktuellen Generate zu referenzieren.
Wie DATAMIMIC Scope-Frames aufbaut¶
Jede Iteration eines <generate> erzeugt einen eigenen Scope-Frame mit:
current_variables: Werte aus<variable>current_product: Werte, die bereits durch<key>oder verschachtelte Kinder geschrieben wurdencurrent_name: der aktuelle<generate name="...">
Die wichtigste Folge davon ist:
- Das erste
<generate>direkt unter<setup>wird in den Top-Level-Namespace eingefaltet. - Ein verschachteltes
<generate>wird unter seinem eigenennameeingefügt. - Der aktuelle Frame ist immer über
thiserreichbar. - In verschachtelten Frames ist die zusammengeführte direkte Parent-Sicht über
this.parenterreichbar.
Die Regel, über die man leicht stolpert¶
Innerhalb eines verschachtelten <generate> bedeutet ein unqualifizierter Name nicht zuverlässig „die Variable, die genau hier deklariert wurde“.
Häufig wird stattdessen gegen den eingefalteten äußeren Frame aufgelöst.
Deshalb kann das hier nach dem Verschieben von Variablen nach innen brechen:
1 | |
Wenn voszData und index vorher im äußeren Frame lagen, liest dieser Ausdruck die äußeren Werte. Nach dem Verschieben in den inneren Frame kann derselbe unqualifizierte Ausdruck weiterhin auf den äußeren Frame zeigen statt auf den neuen lokalen.
Verwende stattdessen:
1 | |
Schnelle Entscheidungshilfe¶
Verwende diese Kurzfassung beim Schreiben oder Prüfen von Ausdrücken:
| Du willst lesen... | Verwende... |
|---|---|
Eine Variable aus dem aktuellen verschachtelten <generate> |
this.varName |
| Eine Variable aus dem direkten Parent-Generate | this.parent.varName |
| Eine Variable über einen expliziten benannten Pfad | child.grand_child.varName |
Eine Variable im ersten Generate unter <setup> |
meistens varName |
Tip
Wenn Du unsicher bist, nimm bevorzugt this.*. Das ist die klarste und sicherste Wahl für lokale verschachtelte Variablen.
Referenztabelle¶
| Wo der Wert deklariert ist | Bevorzugte Referenz | Hinweis |
|---|---|---|
Aktuelles verschachteltes <generate> |
this.varName |
Stabilste Form |
Aktuelles verschachteltes <generate> |
<aktuellerGenerateName>.varName |
Funktioniert für verschachtelte Generates innerhalb eines anderen Generates |
Direktes Parent-<generate> |
this.parent.varName |
Explizit und sicher |
| Eingefalteter äußerer Frame | varName |
Kann funktionieren, ist aber leicht missverständlich |
| Bereits aufgebauter verschachtelter Pfad | child.grand_child.varName |
Nützlich für expliziten Baumzugriff |
Erstes <generate> direkt unter <setup> |
varName oder root.varName |
Meist nicht als root.generateName.varName verfügbar |
Beispiel 1: Mem-Variablen in das innere Generate verschieben¶
Funktioniert, wenn die Variablen im äußeren Frame bleiben¶
1 2 3 4 5 6 7 8 9 10 11 12 | |
Hier wird voszData.data[index] gegen den äußeren Frame aufgelöst und funktioniert deshalb.
Bricht oder liest den falschen Frame nach dem Verschieben nach innen¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
Empfehlung
- Verwende
this.*, wenn die Variable im selben verschachtelten Generate deklariert ist. - Verwende
this.parent.*, wenn Du bewusst den äußeren Frame lesen willst. - Lasse den Ausdruck nach dem Verschieben von Variablen zwischen Frames nicht unqualifiziert.
Beispiel 2: Cascade Generate und this.id¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
Warum this.id hier wichtig ist:
- Das äußere
idkann unqualifiziert bleiben, weil dieser Frame eingefaltet ist. - Innere
id-Werte sollten mitthis.qualifiziert werden, um Shadowing-Verwirrung zu vermeiden. - Wiederverwendete Namen wie
id,indexoderrowsind nur dann sicher, wenn Du explizit bleibst.
Beispiel 3: Benannte Pfade über verschachtelte Generates¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Das zeigt die aktuelle Namespace-Struktur, nicht eine generische „jeden Ancestor per Namen“-Regel:
child.idxfunktioniert, weilchildder eingefaltete Parent-Frame ist.child.grand_child.indexfunktioniert, weilgrand_childunter diesem Frame verschachtelt ist.this.indexbleibt trotzdem die klarste Aussage für „die Variable aus dem aktuellen Generate“.
Beispiel 4: nestedKey-Pfadlogik ist dasselbe Prinzip¶
nestedKey-Pfadlogik folgt derselben Denkweise: Werte werden über den aktuell verfügbaren Baum adressiert, statt anzunehmen, dass automatisch „der nächste Name gewinnt“.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
Konzeptionell:
dataist der benannte Generate-Frame.send_infoist eine darunterliegende verschachtelte Struktur.contact_method.Emailwird über diesen expliziten Pfad aufgelöst.
Die gleiche Empfehlung gilt für verschachtelte Generates: Bevorzuge explizite Pfade, wenn ein Wert nicht offensichtlich lokal ist.
Beispiel 5: Gemeinsam genutzte Variablen außen, lokale Variablen innen¶
Hier wird das Scope-Modell nützlich statt nur fehleranfällig.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
Warum das nützlich ist:
customerbleibt außen, weil beide verschachtelten Generates den Wert brauchen.- Jedes verschachtelte Generate kann trotzdem seinen eigenen lokalen
lineIndexhaben. this.lineIndexmacht sofort klar, dass der Wert lokal zum aktuellen verschachtelten Generate gehört.- Derselbe Hilfsname in zwei verschachtelten Generates bleibt lesbar, wenn Du ihn qualifizierst.
Warum dieses Modell flexibel ist¶
Dieses Scope-Modell gibt Dir zwei nützliche Muster:
- Gemeinsam genutzte äußere Variablen: Deklariere Lookup-Daten, memstore-basierte Werte oder gemeinsame IDs einmal im äußeren Generate und verwende sie in mehreren inneren Generates wieder.
- Lokale innere Variablen: Halte Hilfszähler, temporäre Selektoren oder Formatierungswerte in dem verschachtelten Generate, zu dem sie gehören.
Das Ergebnis ist ein explizites Modell:
- Gemeinsame Werte bleiben gemeinsam.
- Lokale Werte bleiben lokal.
- Verschachtelte Pfade bleiben lesbar.
- Shadowing bleibt beherrschbar, wenn Du
this.*konsequent verwendest.
Best Practices¶
- Bevorzuge
this.*für Variablen, die im aktuellen verschachtelten Generate deklariert sind. - Bevorzuge
this.parent.*, wenn Du absichtlich aus dem direkten Parent-Frame liest. - Halte gemeinsam genutzte memstore-basierte Variablen im äußeren Generate, wenn mehrere innere Generates sie brauchen.
- Wenn Variablen nur für ein verschachteltes Generate lokal sind, halte sie lokal und verwende konsequent
this.*. - Vermeide wiederverwendete Namen wie
id,index,rowoderdataüber mehrere Ebenen hinweg, außer Du qualifizierst sie immer. - Betrachte unqualifizierte Namen in verschachtelten Generates als riskant, auch wenn sie im Moment funktionieren.
Troubleshooting¶
| Symptom | Wahrscheinliche Ursache | Fix |
|---|---|---|
variable not found |
Der Wert existiert nur im aktuellen verschachtelten Frame, aber der Ausdruck wird woanders aufgelöst | Verwende this.varName oder <aktuellerGenerateName>.varName |
| Falscher Wert, aber kein Fehler | Der Ausdruck wurde gegen einen eingefalteten äußeren Frame aufgelöst | Qualifiziere mit this.* oder this.parent.* |
ambiguous variable oder verwirrendes Shadowing |
Derselbe Name existiert in mehreren Frames | Benenne die Variable um oder qualifiziere den Frame explizit |
| Ein Pfad funktioniert auf einer Ebene, aber nicht auf einer anderen | Die Namespace-Struktur hat sich durch die Verschachtelung geändert | Verwende this.* für aktuelle Werte und explizite benannte Pfade für Baumzugriffe |
root.someGenerate.var schlägt fehl |
Das erste Generate unter <setup> wird eingefaltet; dieser Generate-Name existiert dann unter root oft nicht |
Verwende root.var oder den aktuell sichtbaren benannten Pfad |