Event Handling & Cross-Window Kommunikation
Dieses Dokument beschreibt die kanonische Architektur des Event-Systems in p2d2. Es trennt drei Ebenen klar voneinander und definiert, welche Ebene für welche Art der Kommunikation zuständig ist. Die Beschreibung entspricht dem aktuellen Quellcode (src/utils/events.ts, src/utils/cross-window-events.ts, src/components/EventConsole.ts).
Die drei Event-Ebenen
Ebene A: Lokale UI-Interaktion
Lokale Interaktionen innerhalb einer Komponente oder einer Seite nutzen lokale DOM-Events und lokalen Komponenten-State. Sie benötigen keine fachlichen p2d2-Events.
Beispiele aus dem Code:
- Tab-Umschaltung zwischen Kommunen- und Kategorien-Grid in
src/pages/index.astro(klickbasierteswitchTab()-Funktion, Swipe-Gesten auf dem Grid-Container). - Scroll-Verhalten der Karten-Sektion in
src/components/OpenLayersMap.astro(Listener aufp2d2:kommunen:focusundp2d2:category:selectedlösen Scrollen aus;scrollToSelectionHeaderwird global exponiert). - Lokaler Editor-State in
src/components/feature-editor/EditorState.ts(Setter mitnotifyListeners()und reaktivemReactiveEditorState).
Für lokale UI-Interaktion sind direkte DOM-Events ausdrücklich erlaubt und der Standardweg.
Ebene B: Fachliche Hauptfenster-Events (src/utils/events.ts)
Fachliche Ereignisse im Hauptfenster werden über das typisierte Event-System in src/utils/events.ts abgewickelt.
Zentrale Bausteine:
P2D2EventType– Enum aller fachlichen Event-Namen (Präfixp2d2:).P2D2EventMap– typsichere Zuordnung von Event-Typ zu Detail-Interface.dispatchP2D2Event(eventType, detail, { throttleMs })– typsicherer Dispatcher mit Throttling.addP2D2EventListener(eventType, handler, options)– typsicherer Listener mit HMR-Guard.logToEventConsole(eventName, detail, meta)– protokolliert Ereignisse in der EventConsole, sofern diese verfügbar ist.
Eingebaute Robustheit:
- Throttling: Standard-
THROTTLE_MS = 200 ms; pro Event-Typ wird der letzte Dispatch-Zeitpunkt gespeichert. Aufrufer könnenthrottleMs: 0setzen, um Throttling für einen Aufruf zu deaktivieren (z. B.kommunen-click-handlerundKategorienGridfürKOMMUNEN_FOCUS/CATEGORY_SELECTED). - Queue und Retry: Ereignisse werden bei nicht bereitem Event-System in eine Warteschlange gelegt und bis zu
MAX_RETRIES = 3erneut versucht.RETRY_DELAYist als Konstante (250 ms) definiert, wird im aktuell sichtbaren Queue-/Retry-Pfad jedoch nicht verwendet – es wird daher keine garantierte Retry-Verzögerung dokumentiert. Die Queue-Verarbeitung selbst läuft überQUEUE_PROCESS_INTERVAL = 100 ms(setTimeout). - EventConsole-Integration:
dispatchP2D2Eventprotokolliert überlogToEventConsole().
Event-Typen (Auszug der fachlichen Domänen):
- Kommune:
KOMMUNEN_FOCUS,KOMMUNEN_SELECTED - Kategorie:
CATEGORY_SELECTED - Karte:
MAP_READY,MAP_MOVEEND,MAP_ZOOMEND,MAP_CLICK,CRS_CHANGE - Layer:
LAYER_TOGGLE,LAYER_VISIBILITY_CHANGE - WFS:
WFS_LOAD_START,WFS_LOAD_COMPLETE,WFS_LOAD_ERROR,WFS_FEATURE_CREATED,WFS_FEATURE_UPDATED,WFS_FEATURE_DELETED - Editor:
EDITOR_READY,EDITOR_FEATURE_MODIFIED,EDITOR_TOOL_SWITCH,EDITOR_MODE_CHANGE,EDITOR_FEATURE_SELECTED,EDITOR_FEATURE_DESELECTED,EDITOR_SAVE_START,EDITOR_SAVE_COMPLETE,EDITOR_SAVE_ERROR - UI:
UI_PANEL_TOGGLE
Persistenzschlüssel in events.ts:
p2d2_selected_crs(getSelectedCRS/setSelectedCRS)p2d2_selected_kommune(getSelectedKommune/setSelectedKommune)clearSelections()räumt beide Schlüssel.
Ebene C: Cross-Window-Ereignisse (src/utils/cross-window-events.ts)
Ereignisse, die sowohl lokal als auch fensterübergreifend wirken müssen, laufen über die Cross-Window-Bridge in src/utils/cross-window-events.ts.
Zentrale Funktionen:
dispatchCrossWindowEvent(eventType, detail, { crossWindow = true })- Dispatcht das Ereignis lokal als
CustomEvent. - Protokolliert es in der EventConsole mit
source,windowIdund (bei Weiterleitung)crossWindow. - Sendet es bei aktiviertem
crossWindowan verbundene Fenster:- Editor-Fenster → Hauptfenster über
window.opener.postMessage(...). - Hauptfenster → alle registrierten Editor-Fenster über
broadcastToEditorWindows(...).
- Editor-Fenster → Hauptfenster über
- Dispatcht das Ereignis lokal als
initializeCrossWindowBridge()– muss in jedem Fenster (Haupt- und Editor-Fenster) aufgerufen werden. Sie registriert einenmessage-Listener, prüft die Herkunft (event.origin === window.location.origin) und akzeptiert ausschließlich Nachrichten vom Typp2d2:cross-window-event.registerEditorWindow(editorWindow)– registriert ein geöffnetes Editor-Fenster im Hauptfenster; die Registrierung wird beim Schließen des Fensters automatisch entfernt (Intervall-Check, 1000 ms).getWindowId()– liefert die eindeutige Fenster-ID;getWindowType()unterscheidetmainundeditor(isMainWindow()prüft!window.opener).
Eingesetzt wird Ebene C unter anderem von:
WFSLayerManagerfürWFS_LOAD_START,WFS_LOAD_COMPLETE,WFS_LOAD_ERROR.EditorStatefürEDITOR_FEATURE_SELECTED,EDITOR_FEATURE_DESELECTED,EDITOR_TOOL_SWITCH,EDITOR_MODE_CHANGE.MapCanvasfürMAP_READY.EditorAppundGrabflurEditorAppfürEDITOR_READY.
Sicherheitsmodell:
- Same-Origin-Pflicht: Nur Nachrichten der eigenen Herkunft (
window.location.origin) werden verarbeitet. - Nachrichtentyp-Prüfung: Nur
p2d2:cross-window-event-Nachrichten werden angenommen. - Zielsteuerung: Editor-Fenster senden an
window.opener; das Hauptfenster sendet ausschließlich an die registrierten Editor-Fenster.
Verbindliche Grenzen
dispatchP2D2Event()ist der Standard für fachliche Hauptfenster-Events.dispatchCrossWindowEvent()ist für Ereignisse vorgesehen, die lokale und fensterübergreifende Kommunikation benötigen.- Direkte DOM-Events sind für lokale UI-Interaktion erlaubt (Ebene A).
- Direkte
window.dispatchEvent()-Aufrufe sind kein allgemeines Muster für neue Funktionen. Sie treten nur in bestehenden, dokumentierten Sonder- oder Fallbackpfaden auf – beispielsweise imKommunenClickHandler, wenn der typisierte Dispatcher fehlschlägt:
// src/utils/kommunen-click-handler.ts – dokumentierter Fallback
try {
dispatchP2D2Event(P2D2EventType.KOMMUNEN_FOCUS, detail, { throttleMs: 0 });
} catch (error) {
setTimeout(() => {
window.dispatchEvent(
new CustomEvent(P2D2EventType.KOMMUNEN_FOCUS, { detail }),
);
}, 100);
}- Die EventConsole protokolliert nur Vorgänge, die
logToEventConsole()erreichen. Sie beobachtet nicht automatisch beliebige DOM-Events.
Auswahl- und Kartenpfade (Kurzübersicht)
KommunenGrid
→ KommunenClickHandler
→ dispatchP2D2Event(P2D2EventType.KOMMUNEN_FOCUS, detail, { throttleMs: 0 })
→ MapCanvas-Listener (addP2D2EventListener)
→ mapState.setSelectedKommune(detail)
→ CRS-, Center- oder BBOX-Navigation
KategorienGrid
→ mapState.setSelectedCategory(categorySlug)
→ dispatchP2D2Event(P2D2EventType.CATEGORY_SELECTED, detail, { throttleMs: 0 })
→ Scrollen zur Kartenansicht
mapState-Änderung
→ WFSLayerManager-Subscription
→ WFS-Layer laden oder leeren
→ dispatchCrossWindowEvent(WFS_LOAD_START | WFS_LOAD_COMPLETE | WFS_LOAD_ERROR)Editorpfad (Kurzübersicht)
OpenLayers-Klick auf ein passendes Feature
→ FeaturePopupHandler
→ WFS-Prüfung auf Grabflur-Daten
→ Informationsdialog oder window.open() für den Feature-Editor
→ registerEditorWindow()
→ Cross-Window-Kommunikation zwischen Haupt- und EditorfensterDer generische Feature-Editor und der Grabflur-Editor sind getrennte Anwendungen beziehungsweise Abläufe. Der Grabflur-Editor ist rollenbeschränkt und wird über /verwaltung/grabflur-editor aufgerufen; er bestimmt Kommune und räumlichen Kontext aus der authentifizierten Session und deren Metadaten.
Debugging mit der EventConsole
Die EventConsole (src/components/EventConsole.ts) ist ein Overlay zur Live-Beobachtung protokollierter p2d2-Events.
Aktivierung und Bedienung:
- URL-Parameter:
?debug=eventsaktiviert die Konsole (in Produktion ist sie ohne diesen Parameter deaktiviert; im Dev-Modus ist sie grundsätzlich aktivierbar). - Tastenkürzel:
Ctrl+Shift+E(Windows/Linux) bzw.Cmd+Shift+E(Mac) toggelt die Konsole – das Kürzel ist unabhängig vom Aktivierungszustand registriert. - Filter: Textfeld zur Filterung der Logs.
- Clear: leert alle Log-Einträge.
- Export: „Copy JSON“-Button exportiert die Logs als JSON.
Eigenschaften:
STORAGE_KEY = "p2d2:debug:events"– speichert{ visible, timestamp }; der Zustand wird nur wiederhergestellt, wenn er jünger als 24 Stunden ist.maxLogs = 50– ältere Einträge werden verworfen.- Jeder Log-Eintrag enthält Zeitstempel, Event-Typ, Detail und optionale Metadaten (
source,windowId,crossWindow,retryCount,throttled,success,error). - Die Konsole wird über
window.__P2D2_EVENT_CONSOLE__angesprochen;logToEventConsole()prüft genau dieses globale Objekt.
Dokumentierte technische Beobachtungen
initializeCrossWindowBridge()wird in den Editor-Einstiegspunkten mehrfach aufgerufen (unter anderem im Frontmatter und im Skript vonsrc/pages/feature-editor/[featureId].astrosowie inGrabflurEditorApp.init()). Diese Aufgabe dokumentiert den Ist-Zustand; der Bridge-Code wird nicht verändert.
Änderungshistorie
| Version | Datum | Änderung |
|---|---|---|
| 1.0 | 2026-08-06 | Dokumentation am aktuellen Quellcode ausgerichtet; frühere, nicht mehr belegbare Aussagen entfernt oder als historisch markiert. |
| 1.1 | 2026-08-06 | RETRY_DELAY als im Queue-Pfad ungenutzte Konstante präzisiert (externer Review). |