enhancement
major
minor
defect
critical
major
minor
major
#30132
Ebene anzeigen: Die Wert-Listener des Modells (<value-listeners>) reagieren über einen UI-unabhängigen Listener-Vertrag auf Feldänderungen im React-Formular.
Problem
In einer React-View-Ebene <form> werden Optionen, dynamische Sichtbarkeit, Einschränkungen und abgeleitete (schreibgeschützte) Felder neu berechnet, wenn sich ein Feld ändert, auf das sie zugreifen (FormControl.FieldChangeListener, AttributeFieldModel, Artikelformulare der View-Ebene). Es gibt keine Möglichkeit, dass eine Änderung an einem bearbeitbaren Feld ein anderes bearbeitbares Feld desselben bearbeiteten Objekts zurücksetzt oder neu berechnet: <form> und <field> verfügen über keinen Änderungshandler, keine Modellannotation schreibt bei einer Änderung eines Attributs ein anderes, und der klassische ValueListener-Mechanismus wird vom React-Formular nicht verwendet. Auf Kanalebene füllt ein <channel init=".." inputs=".."> bei einer Änderung seiner Eingaben einen der Ansicht gehörenden Wert neu ( Artikelkanäle), aber ein <field attribute> kann nicht an einen Kanal gebunden werden, und Kanäle erkennen die nicht gespeicherten Bearbeitungen des Formulars nicht.
Anwendungsfall
Ein Dialog zum Anlegen eines Projekts wählt zunächst ein Repository, dann einen Branch und ein Modul dieses Repositorys aus. Wenn der Benutzer das Repository ändert, sind der Branch und das Modul, die für das vorherige Repository eingegeben wurden, nicht mehr relevant und sollten gelöscht (oder auf ihre Standardwerte zurückgesetzt) werden. Derzeit kann die Anwendung den verbleibenden Wert in dem entsprechenden Feld lediglich als ungültig markieren.
Vorschlag
Eine deklarative Methode zum Zurücksetzen eines Feldes, wenn sich die Felder ändern, von denen es abhängt, z. B. eine Modellannotation für das abhängige Attribut, die einen Rücksetzwert als TL-Skript über das bearbeitete Objekt angibt (Objekt -> null oder Objekt -> defaultBranch($object)), die vom Formular ausgewertet wird, sobald eines der vom Skript gelesenen Attribute – oder eine explizit benannte Liste von Attributen – vom Benutzer geändert wird. Es schreibt wie eine Benutzerbearbeitung in das Overlay des Formulars, sodass Dirty-Tracking, Einschränkungen und das Speichern wie gewohnt funktionieren, und es wird beim ersten Laden des Formulars niemals ausgelöst.
Lösung
Anstelle einer neuen Reset-Annotation wird die vorhandene Modell-Annotation <value-listeners> (TLValueListeners) für beide Oberflächen nutzbar. Sie befindet sich am auslösenden Attribut; ein <script>-Listener (ValueListenerByExpression, object -> newValue -> oldValue -> attribute) schreibt beliebige andere Attribute über $object.set(...), z. B. am Attribut „repository“: p -> new -> old -> { $p.set(`branch, null); $p.set(module, null) }`.
- UI-unabhängige Schnittstelle in com.top_logic: AttributeValueListener.valueChanged(TLFormObjectBase object, TLStructuredTypePart attribute, Object oldValue, Object newValue). Er ist der Elementtyp von TLValueListeners.getListeners(); Tag-Namen bleiben erhalten, bestehende Modelle bleiben gültig. TLFormObjectBase ist die gemeinsame Schnittstelle des klassischen TLFormObject und desReact-TLObjectOverlay.
- ValueListenerByExpression implementiert nur noch diesen Vertrag (kein FormField mehr).
- Klassische Oberfläche: DefaultAttributeFormFactory hängt die Listener über AttributeValueListenerAdapter (com.top_logic.element) an das Feld an; das Verhalten bleibt unverändert.
- React-Oberfläche: ValueListenerDispatch ruft die Listener des geänderten Attributs bei einer Benutzeränderung und bei einer Kaskade auf, nicht beim Laden, beim Verwerfen, beim Speichern oder für Werte, die ein Dialog übernimmt. Pro Attribut laufen die Listener höchstens einmal pro Kette (Schutz vor Zyklen). Dies gilt für das Formularobjekt und für die Zeilen einer <composition-table>.
- Overlay-Änderungen werden sichtbar: Ein Schreibvorgang in das TLObjectOverlay (tUpdate/`revert`) aktualisiert das Feld des Attributs und meldet die Änderung wie eine Benutzeränderung: Dirty-Status, Constraints, Optionen, Sichtbarkeit und abgeleitete Felder folgen.
- Neue Zeilen einer `<composition-table>` sind ebenfalls Overlays (über dem neuen transienten Objekt) wie die bestehenden Zeilen; Anlegen, Bearbeiten, Entfernen und Speichern verhalten sich unverändert.
- Beschrieben im Entwicklerartikel view-layer/forms („Reacting to a field change“); Beispiel in der React-Demo (Demos → Constraint-Test: Die Kategorie löscht den Code, der Name einer Position löscht deren Preis).
Migration
Eine eigene Java-Klasse, die com.top_logic.layout.form.ValueListener implementiert und in einer Modell-Annotation <value-listeners> konfiguriert ist, muss stattdessen com.top_logic.model.annotate.AttributeValueListener implementieren: valueChanged(TLFormObjectBase object, TLStructuredTypePart attribute, Object oldValue, Object newValue) erhält das bearbeitete Objekt (Formular-Overlay) und das Attribut anstelle des FormField. Modelldateien und <script>-Listener müssen nicht geändert werden.