unsaved_changes_forms 0.0.1
unsaved_changes_forms: ^0.0.1 copied to clipboard
reactive_forms adapter for unsaved_changes — track a whole FormGroup for unsaved edits with no per-field configuration.
unsaved_changes_forms #
reactive_forms adapter for unsaved_changes.
Tracks a whole FormGroup for unsaved edits with no per-field configuration.
For a form-shaped surface this is often the only detector you need.
FormGroupDetector<Sources, InvoiceChange>(
id: 'form',
formOf: (s) => s.form,
ignore: {'searchQuery', 'items.*.uiOnly'},
equalsFor: (path) => path == 'notes' ? nullableStringEquals : null,
kindOf: (path) => switch (path) {
'number' || 'date' => InvoiceChange.header,
'notes' => InvoiceChange.notes,
_ when path.startsWith('items.') => InvoiceChange.items,
_ => null, // ignored
},
)
How it works #
Every leaf control becomes a dotted path — number, supplier.id,
items.0.quantity — and each path's value is compared against the baseline.
kindOfmaps a path to a change kind. Returningnullignores the path.- Paths that map to the same
(kind, subject)collapse into one change, so editing three header fields reports one change, not three. subjectOfsplits a kind back apart when you want one change per row.ignoreskips paths entirely.*stands for exactly one segment, soitems.*.uiOnlyskips a per-row UI flag on every row without spanning a dot.equalsForoverrides comparison per path — most usefullynullableStringEquals, so a text field cleared back to''when it startednullis not reported as an edit.- Adding or removing a control from a
FormArrayis a change, because its paths appear or disappear.
No manual subscription: the detector already returns the form's valueChanges,
and the tracker coalesces a typing burst into a single publish.
Disabled controls are included. A field the surface disables mid-edit still holds a value the user may have set, and dropping it would under-report.
License #
MIT