Define isChanged() before first snapshot + emit form-changed on control changes
isChanged() compared against a fabricated '{}' baseline before the first
snapshot() call, so the first query on any non-empty form reported true.
Consumers that embed the boolean in their own baseline (settings dirty
guards) froze that false positive in. Now reports false until a baseline
exists; hasSnapshot getter exposes the state.
New: form listens (capture phase, so non-bubbling events are caught) to
the tp-* control change events and re-emits them as 'form-changed'
(detail.changed = isChanged()), scoped to its own registered controls.
Consumers can drop polling.
This commit is contained in:
1 parent
354b0ab720
commit
82adb306b6
2 files changed
+65
-3
No files matched your search
+64
-2
@@ -7,6 +7,22 @@ This program is available under Apache License Version 2.0
|
||||
import { LitElement, html, css } from 'lit';
|
||||
import { clone } from '@tp/helpers/clone.js';
|
||||
|
||||
// Events that indicate a form control's value changed. Fired either by
|
||||
// tp-* controls (see the control's source for the exact name) or natively
|
||||
// (input/change from wrapped native fields). Listened to in the capture
|
||||
// phase so events that don't bubble (e.g. tp-color-picker's 'change') are
|
||||
// caught as well.
|
||||
const CHANGE_EVENTS = [
|
||||
'input',
|
||||
'change',
|
||||
'value-changed',
|
||||
'selection-changed',
|
||||
'checked-changed',
|
||||
'toggled',
|
||||
'number-changed',
|
||||
'slider-changed',
|
||||
];
|
||||
|
||||
class TpForm extends LitElement {
|
||||
static get styles() {
|
||||
return [
|
||||
@@ -78,6 +94,7 @@ class TpForm extends LitElement {
|
||||
super();
|
||||
this._origValues = [];
|
||||
this._wasSetReadonly = [];
|
||||
this._onControlChanged = this._onControlChanged.bind(this);
|
||||
|
||||
this._addElement = this._addElement.bind(this);
|
||||
this._removeElement = this._removeElement.bind(this);
|
||||
@@ -101,6 +118,11 @@ class TpForm extends LitElement {
|
||||
this.addEventListener('form-element-register', this._addElement);
|
||||
this.addEventListener('form-element-unregister', this._removeElement);
|
||||
|
||||
// Notify about control changes via `form-changed` — see CHANGE_EVENTS.
|
||||
for (const name of CHANGE_EVENTS) {
|
||||
this.addEventListener(name, this._onControlChanged, true);
|
||||
}
|
||||
|
||||
// Holds all custom elements registered.
|
||||
this._controls = [];
|
||||
}
|
||||
@@ -109,6 +131,10 @@ class TpForm extends LitElement {
|
||||
super.disconnectedCallback();
|
||||
this.removeEventListener('form-element-register', this._addElement);
|
||||
this.removeEventListener('form-element-unregister', this._removeElement);
|
||||
|
||||
for (const name of CHANGE_EVENTS) {
|
||||
this.removeEventListener(name, this._onControlChanged, true);
|
||||
}
|
||||
}
|
||||
|
||||
_handleSlotChange(e) {
|
||||
@@ -351,10 +377,46 @@ class TpForm extends LitElement {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if some form control's value was changed.
|
||||
* True once snapshot() was called at least once. Dirty checks are only
|
||||
* meaningful against a baseline.
|
||||
*/
|
||||
get hasSnapshot() {
|
||||
return this._snapshot !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if some form control's value was changed since the last
|
||||
* snapshot(). Before the first snapshot() there is no baseline to
|
||||
* compare against, so this reports false instead of comparing against a
|
||||
* fabricated empty form.
|
||||
*/
|
||||
isChanged() {
|
||||
return JSON.stringify(this.serialize()) !== (this._snapshot || '{}');
|
||||
if (!this.hasSnapshot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return JSON.stringify(this.serialize()) !== this._snapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if el is a control registered with this form (custom control or
|
||||
* unwrapped native element). Scopes form-changed to the form's own
|
||||
* controls when forms are nested.
|
||||
*/
|
||||
_isRegisteredControl(el) {
|
||||
return this._controls.includes(el) || Array.from(this._nativeElements).includes(el);
|
||||
}
|
||||
|
||||
_onControlChanged(e) {
|
||||
if (!this._isRegisteredControl(e.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.dispatchEvent(new CustomEvent('form-changed', {
|
||||
detail: { changed: this.isChanged() },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user