feat(isdirty): Navigations-Guard gegen ungespeichertes Verlassen von Bearbeitungsmasken
Zentraler DirtyGuard (dirtyguard.ts) mit DirtyMaskHelper-Klasse und 3-Button-Bestätigungs-Modal (Speichern/Verwerden/Abbrechen). Form meldet Dirty-State via onDirtyChange nach oben; input-Event ergänzt change für frühzeitiges Dirty-Marking. Alle App-Navigationen (Breadcrumb, Menü, In-Masken-Buttons, Abbrechen) laufen über guardedNavigate. Auto-Redraw bei dirty per e.redraw=false unterbunden, damit DOM-Eingaben erhalten bleiben (Input-Widget hat kein Live-Binding). Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
1 parent
fd30049a32
commit
a67921ec74
10 files changed
+462
-120
No files matched your search
@@ -12,6 +12,13 @@ export interface iForm {
|
||||
stickyButtons?: boolean
|
||||
/** Die Funktion gibt das HTMLFormElement zurück, welches in der Form-Componente angelegt wird */
|
||||
onformcreate?: (form:HTMLFormElement) => void
|
||||
/** Callback bei Änderung des Dirty-State. Wird mit true aufgerufen,
|
||||
* sobald der Nutzer Eingaben vornimmt, und mit false, nachdem das
|
||||
* Formular erfolgreich gespeichert oder zurückgesetzt wurde. Die
|
||||
* umgebende Maske nutzt dies, um ihren eigenen isDirty-State mit dem
|
||||
* der Form synchron zu halten und so den Navigations-Guard
|
||||
* (dirtyguard.ts) zu speisen. */
|
||||
onDirtyChange?: (dirty: boolean) => void
|
||||
}
|
||||
|
||||
export const Form:m.ClosureComponent<iForm> = (vnode) => {
|
||||
@@ -29,6 +36,9 @@ export const Form:m.ClosureComponent<iForm> = (vnode) => {
|
||||
}
|
||||
function onchanged (e:Event) {
|
||||
isDirty = true
|
||||
if (vnode.attrs.onDirtyChange != undefined) {
|
||||
vnode.attrs.onDirtyChange(true)
|
||||
}
|
||||
if (vnode.attrs.onchanged != undefined) {
|
||||
vnode.attrs.onchanged(e)
|
||||
}
|
||||
@@ -37,6 +47,9 @@ export const Form:m.ClosureComponent<iForm> = (vnode) => {
|
||||
e.preventDefault()
|
||||
if (vnode.attrs.onsubmit(form) == true) {
|
||||
isDirty = false
|
||||
if (vnode.attrs.onDirtyChange != undefined) {
|
||||
vnode.attrs.onDirtyChange(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
function registerFormElement (f:HTMLFormElement) {
|
||||
@@ -46,22 +59,24 @@ export const Form:m.ClosureComponent<iForm> = (vnode) => {
|
||||
}
|
||||
function cancel(e:Event) {
|
||||
e.preventDefault()
|
||||
if (canCloseForm() == true) {
|
||||
isDirty = false
|
||||
if (vnode.attrs.oncancel != undefined) {
|
||||
vnode.attrs.oncancel(e)
|
||||
}
|
||||
else {
|
||||
history.back()
|
||||
}
|
||||
// Der Dirty-Check beim Abbrechen erfolgt jetzt durch den
|
||||
// Navigations-Guard der Maske (dirtyguard.ts), da oncancel die
|
||||
// Navigation auslöst. Die Form selbst fragt hier nicht mehr nach,
|
||||
// um eine doppelte Bestätigung zu vermeiden. isDirty wird nicht
|
||||
// zurückgesetzt, damit der Guard den aktuellen State sieht.
|
||||
if (vnode.attrs.oncancel != undefined) {
|
||||
vnode.attrs.oncancel(e)
|
||||
}
|
||||
else {
|
||||
e['redraw'] = false
|
||||
history.back()
|
||||
}
|
||||
}
|
||||
function reset(e:Event) {
|
||||
if (canCloseForm("reset")) {
|
||||
isDirty = false
|
||||
if (vnode.attrs.onDirtyChange != undefined) {
|
||||
vnode.attrs.onDirtyChange(false)
|
||||
}
|
||||
// Hier passiert nichts.
|
||||
// Da mithril nach jedem User-Event (wie z.B. einem 'Click') ein redraw() ausführt, wird hier die Form
|
||||
// neu gezeichnet und zwar mit den ursprünglichen Daten aus den zu grundeliegenden Datenmodel, das ja
|
||||
@@ -109,6 +124,10 @@ export const Form:m.ClosureComponent<iForm> = (vnode) => {
|
||||
for (let index = 0; index < elements.length; index++) {
|
||||
const element = elements[index];
|
||||
element.addEventListener("change", onchanged)
|
||||
// 'input' feuert schon während des Tippens (vor
|
||||
// Blur), so dass die Maske dirty wird, auch wenn
|
||||
// der Nutzer sofort eine Breadcrumb klickt.
|
||||
element.addEventListener("input", onchanged)
|
||||
}
|
||||
registerFormElement(form)
|
||||
}
|
||||
@@ -131,6 +150,10 @@ export const Form:m.ClosureComponent<iForm> = (vnode) => {
|
||||
for (let index = 0; index < elements.length; index++) {
|
||||
const element = elements[index];
|
||||
element.addEventListener("change", onchanged)
|
||||
// 'input' feuert schon während des Tippens (vor
|
||||
// Blur), so dass die Maske dirty wird, auch wenn
|
||||
// der Nutzer sofort eine Breadcrumb klickt.
|
||||
element.addEventListener("input", onchanged)
|
||||
}
|
||||
registerFormElement(form)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user