DocxFormFlattener¶
Der DocxFormFlattener wandelt ein strukturiertes flexiDocument in ein flaches JavaScript-Objekt um. Dieses Objekt dient als Template-Kontext für docx-templates — d.h. jede {variable} im Word-Template greift direkt auf dieses Objekt zu.
Quellcode: src/app/_helpers/docxFormFlattener.ts
Verarbeitungsreihenfolge¶
flexiDocument
└── chapters[]
└── flexiChapter
├── multiChapter = true → subChapters[] (rekursiv)
├── multiSection = true → subSections[] (Sections-Array)
└── normal → sections[]
└── flexiSection
└── components[]
└── FieldConfig → getypter Wert
Einstiegspunkt ist flattenForm(document). Das Ergebnis ist ein FlattenedForm-Objekt ({ [key: string]: any }), das direkt als Kontext an docx-templates übergeben wird.
Kapitel-Typen¶
Normales Kapitel (multiChapter=false, multiSection=false)¶
Jeder Abschnitt wird direkt unter dem Kapitelnamen abgelegt:
flattened[kapitelName][abschnittName][komponentenName] = { value, rulesResult, ... }
Zugriff im Template:
+++INS kapitelName.abschnittName.komponentenName.value+++
Zusätzlich werden am Kapitelobjekt folgende Metadaten gesetzt:
| Eigenschaft | Typ | Beschreibung |
|---|---|---|
Valid |
boolean |
true wenn chapter.state == 2 |
Title |
string |
Übersetzter Kapiteltitel |
Name |
string |
Übersetzter Kapitelname |
ChapterCount |
number |
1-basierter Kapitelindex |
ChapterTypes |
string[] |
Kapitel-Typ-Array aus der Konfiguration |
SectionTypes |
string[] |
Abschnitts-Typ-Array aus der Konfiguration |
+++INS kapitelName.Valid+++
+++INS kapitelName.Title+++
MultiSection-Kapitel (multiSection=true)¶
Abschnitte landen in einem Sections-Array. Jeder Abschnitt enthält ein Components-Array, in dem jede Komponente als eigenes Objekt gespeichert ist:
flattened[kapitelName].Sections = [
{
Components: [
{ komponentenName: { value, rulesResult, coordinate, name, ChapterCount } },
...
],
Valid: boolean,
Title: string,
Name: string,
Coordinate: string, // z.B. "kapitel.abschnitt"
SectionType: string
},
...
]
Iteration im Template:
+++FOR section IN kapitelName.Sections+++
Abschnitt: +++INS $section.Title+++ — Gültig: +++INS $section.Valid+++
+++FOR cmp IN $section.Components+++
+++INS $cmp.komponentenName.value+++
+++END-FOR cmp+++
+++END-FOR section+++
MultiChapter (multiChapter=true)¶
Das Kapitel wird zu einem Array von Unterkapiteln. Jedes Element enthält die Inhalte des jeweiligen Unterkapitels (ohne den Unterkapitelnamen als Wrapper):
flattened[kapitelName] = [
{ abschnittName: { komponentenName: { value, ... } }, ... }, // Unterkapitel 1
{ abschnittName: { komponentenName: { value, ... } }, ... }, // Unterkapitel 2
...
]
// Das Array hat zusätzlich benannte Eigenschaften:
// .Valid, .Title, .Name, .ChapterCount, .ChapterTypes, .SectionTypes
Iteration im Template:
+++FOR unterkapitel IN kapitelName+++
+++INS $unterkapitel.abschnittName.komponentenName.value+++
+++END-FOR unterkapitel+++
Array-Metadaten
Valid, Title etc. werden als benannte Eigenschaften auf dem Array gesetzt (JavaScript-spezifisch). Im Template sind sie über +++INS kapitelName.Valid+++ erreichbar — außerhalb der Schleife.
Komponenten-Typen und Zugriffsmuster¶
flexiInput¶
Zahlen (input_type='number') werden automatisch mit dem aktuellen Locale formatiert.
{
value: string,
rulesResult: boolean, // Validierungsergebnis
coordinate: string, // z.B. "kapitel.abschnitt.feld"
name: string,
ChapterCount: number
}
Template: +++INS kapitel.abschnitt.feld.value+++
flexiCalculatedInput¶
Berechnete Felder. Wenn decimalPlaces in den Settings gesetzt ist, wird der Wert entsprechend gerundet/formatiert.
{
value: string, // bereits formatierter Anzeigewert
rulesResult: boolean,
coordinate: string,
name: string,
ChapterCount: number
}
Template: +++INS kapitel.abschnitt.feld.value+++
flexiSelect / flexiSwitch¶
{
value: string | boolean,
rulesResult: boolean,
coordinate: string,
name: string,
ChapterCount: number
}
Template: +++INS kapitel.abschnitt.feld.value+++
flexiMultiselect¶
Wenn keine Auswahl getroffen wurde, ist der Wert ein leeres Array [].
Mit Auswahl:
{
selectedCount: number,
value: any[], // Array der ausgewählten Optionen
rulesResult: boolean,
coordinate: string,
name: string,
ChapterCount: number
}
Anzahl ausgewählter Werte: +++INS kapitel.abschnitt.feld.selectedCount+++
Iteration über Werte:
+++FOR v IN kapitel.abschnitt.feld.value+++
+++INS $v+++
+++END-FOR v+++
flexiTextArea¶
Der Text wird zeilenweise aufgeteilt — das Ergebnis ist ein String-Array (eine Zeile pro Eintrag):
["Zeile 1", "Zeile 2", "Zeile 3"]
Iteration im Template (eine Zeile pro Absatz):
+++FOR zeile IN kapitel.abschnitt.feld+++
+++INS $zeile+++
+++END-FOR zeile+++
flexiPictureCollection¶
Der value-String wird als JSON geparst. Das Ergebnis ist ein Array von Bild-Objekten mit coordinate und ChapterCount als benannte Array-Eigenschaften.
[
{ /* Bildobjekt aus dem IndexedDB/Storage */ },
...
]
Iteration:
+++FOR bild IN kapitel.abschnitt.feld+++
+++INS $bild.fileUrl+++
+++END-FOR bild+++
flexiPdfCollection / flexiPdfStore¶
Wie flexiPictureCollection — value kann entweder ein JSON-String oder bereits ein Array/Objekt sein (beide Fälle werden behandelt). Ergebnis ist ein Array mit coordinate und ChapterCount.
Iteration:
+++FOR pdf IN kapitel.abschnitt.feld+++
+++INS $pdf.name+++ — +++INS $pdf.fileUrl+++
+++END-FOR pdf+++
flexiDynamicTable¶
Eine dynamische Tabelle erzeugt drei Schlüssel im flattened-Objekt:
| Schlüssel | Typ | Inhalt |
|---|---|---|
feld |
object[] |
Rohdaten: [{col1: val1, col2: val2}, ...] |
feld_rows |
any[][] |
Zeilen inkl. Header-Zeile: [[h1, h2], [v1, v2], ...] |
feld_columns |
any[][] |
Spalten inkl. Header: [[h1, v1, v2], [h2, v1, v2], ...] |
Rohzugriff (Objekte iterieren):
+++FOR row IN kapitel.abschnitt.feld+++
+++INS $row.spalte1+++ — +++INS $row.spalte2+++
+++END-FOR row+++
Zeilenweise (mit Header):
+++FOR row IN kapitel.abschnitt.feld_rows+++
+++FOR zelle IN $row+++
+++INS $zelle+++
+++END-FOR zelle+++
+++END-FOR row+++
Spaltenweise:
+++FOR col IN kapitel.abschnitt.feld_columns+++
+++FOR zelle IN $col+++
+++INS $zelle+++
+++END-FOR zelle+++
+++END-FOR col+++
ChapterCount bei DynamicTable
Der Zähler wird unter feld_ChapterCount abgelegt (nicht als Eigenschaft des Arrays), da das Array-Objekt selbst die Rohdaten enthält.
flexiKunden¶
Ein Sondertyp — alle Eigenschaften des value-Objekts werden direkt in den übergeordneten Abschnitt geflattent (kein Wrapper-Objekt):
// component.value = { firma: "ACME", ansprechpartner: "Max" }
// wird zu:
flattened[abschnittName]["firma"] = "ACME"
flattened[abschnittName]["ansprechpartner"] = "Max"
Direktzugriff im Template: +++INS kapitel.abschnitt.firma+++
Ignorierte Typen¶
Folgende Komponenten erzeugen keinen Eintrag im flattened-Objekt:
flexiLineflexiHeaderflexiHTMLflexiLabel
Sonderfelder auf oberster Ebene¶
Revisionsverlauf¶
Wenn document.revisioningEnabled == true und document.revision gesetzt ist, wird revisionHistory als sortiertes Array auf der obersten Ebene abgelegt:
flattenedData["revisionHistory"] = [
{ date, author, comment, ... }, // älteste zuerst
...
]
Template:
+++FOR rev IN revisionHistory+++
+++INS $rev.date+++ — +++INS $rev.author+++: +++INS $rev.comment+++
+++END-FOR rev+++
Abschnitts-Metadaten¶
Jeder Abschnitt erhält folgende Metadaten, unabhängig vom Typ:
| Eigenschaft | Typ | Beschreibung |
|---|---|---|
Valid |
boolean |
true wenn section.state == 2 |
Title |
string |
Übersetzter Abschnittstitel |
Name |
string |
Übersetzter Abschnittsname |
Coordinate |
string |
Pfad im Format "kapitelName.abschnittName" |
Zugriff bei normalem Abschnitt: +++INS kapitel.abschnitt.Valid+++
Zugriff im MultiSection-Loop: +++INS $section.Valid+++ (innerhalb von +++FOR section IN kapitel.Sections+++)
Vollständiges Beispiel¶
Gegeben folgende Dokumentstruktur:
Kapitel: "pruefbericht" (normal)
Abschnitt: "allgemein"
flexiInput: "auftragsnummer"
flexiSelect: "status"
flexiTextArea: "bemerkungen"
Abschnitt: "messungen" (multiSectionType)
flexiInput: "messwert"
flexiInput: "einheit"
Ergebnis in flattenedData:
{
pruefbericht: {
allgemein: {
auftragsnummer: { value: "A-2024-001", rulesResult: true, ... },
status: { value: "abgeschlossen", rulesResult: true, ... },
bemerkungen: ["Zeile 1", "Zeile 2"],
Valid: true,
Title: "Allgemeine Angaben",
Name: "allgemein",
Coordinate: "pruefbericht.allgemein"
},
Sections: [
{
Components: [
{ messwert: { value: "12.5", ... } },
{ einheit: { value: "mm", ... } }
],
Valid: true,
Title: "Messung 1",
Coordinate: "pruefbericht.messung_1"
}
],
Valid: true,
Title: "Prüfbericht"
}
}
Word-Template-Ausschnitt:
Auftragsnummer: +++INS pruefbericht.allgemein.auftragsnummer.value+++
Status: +++INS pruefbericht.allgemein.status.value+++
Bemerkungen:
+++FOR zeile IN pruefbericht.allgemein.bemerkungen+++
+++INS $zeile+++
+++END-FOR zeile+++
Messungen:
+++FOR section IN pruefbericht.Sections+++
+++INS $section.Title+++
+++FOR cmp IN $section.Components+++
+++INS $cmp.messwert.value+++ +++INS $cmp.einheit.value+++
+++END-FOR cmp+++
+++END-FOR section+++