@firestitch/form 18.0.34 → 18.0.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/components/form-dialog-actions/form-dialog-actions.component.d.ts +19 -7
- package/app/directives/form-base/form-base.directive.d.ts +7 -0
- package/app/directives/validators/control.directive.d.ts +14 -1
- package/esm2022/app/components/form-dialog-actions/form-dialog-actions.component.mjs +28 -11
- package/esm2022/app/directives/form-base/form-base.directive.mjs +1 -1
- package/esm2022/app/directives/validators/control.directive.mjs +42 -11
- package/fesm2022/firestitch-form.mjs +68 -20
- package/fesm2022/firestitch-form.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1589,11 +1589,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImpor
|
|
|
1589
1589
|
}] } });
|
|
1590
1590
|
|
|
1591
1591
|
class FsFormDialogActionsComponent {
|
|
1592
|
-
/** Leave unset
|
|
1592
|
+
/** Leave unset to follow whether the enclosing form or container has anything to save. */
|
|
1593
1593
|
save;
|
|
1594
1594
|
create = false;
|
|
1595
1595
|
close = false;
|
|
1596
|
-
/** Leave unset
|
|
1596
|
+
/** Leave unset to fill in whenever Save does not apply. */
|
|
1597
1597
|
done;
|
|
1598
1598
|
closeData = null;
|
|
1599
1599
|
name;
|
|
@@ -1607,20 +1607,37 @@ class FsFormDialogActionsComponent {
|
|
|
1607
1607
|
_form = this._ownDialogOnly(inject(FsFormDirective, { optional: true }));
|
|
1608
1608
|
_cdRef = inject(ChangeDetectorRef);
|
|
1609
1609
|
/**
|
|
1610
|
-
*
|
|
1611
|
-
*
|
|
1612
|
-
*
|
|
1613
|
-
|
|
1614
|
-
|
|
1610
|
+
* Whatever owns the form set these actions act on, or null when nothing does.
|
|
1611
|
+
* A container answers for every form mounted under it; failing that the
|
|
1612
|
+
* enclosing form's root answers for itself and anything linked into it.
|
|
1613
|
+
*/
|
|
1614
|
+
get _owner() {
|
|
1615
|
+
return this._container || this._form?.rootForm || null;
|
|
1616
|
+
}
|
|
1617
|
+
/**
|
|
1618
|
+
* Save and Done are one decision - whether there is anything to save - rather
|
|
1619
|
+
* than two independent switches, so a bare `<fs-form-dialog-actions>` resolves
|
|
1620
|
+
* it off the form set it sits in and needs no bindings at all. A footer in a
|
|
1621
|
+
* container swaps Save for Done as tabs change without the dialog listing
|
|
1622
|
+
* which tabs are savable, and a dialog with no form at all - a read-only one
|
|
1623
|
+
* of labelled facts - gets Done, because a submit button there has nothing to
|
|
1624
|
+
* submit.
|
|
1625
|
+
*
|
|
1626
|
+
* An explicit binding always wins. `[done]="true"` on its own means Done and
|
|
1627
|
+
* only Done: turning Done on while leaving a Save beside it is never what the
|
|
1628
|
+
* caller meant, and the two-flag version of this let that happen silently.
|
|
1615
1629
|
*/
|
|
1616
1630
|
get showSave() {
|
|
1617
|
-
|
|
1631
|
+
if (this.save !== undefined) {
|
|
1632
|
+
return this.save;
|
|
1633
|
+
}
|
|
1634
|
+
return this.done === true ? false : this._owner?.submits ?? false;
|
|
1618
1635
|
}
|
|
1619
1636
|
get showDone() {
|
|
1620
|
-
return this.done ?? (
|
|
1637
|
+
return this.done ?? (!this.showSave && !this.create);
|
|
1621
1638
|
}
|
|
1622
1639
|
ngOnInit() {
|
|
1623
|
-
const owner = this.
|
|
1640
|
+
const owner = this._owner;
|
|
1624
1641
|
if (owner) {
|
|
1625
1642
|
// Track the whole set, not one form's own controls: with a form scoped to a
|
|
1626
1643
|
// tab's fields, the actions sit outside it in the dialog footer and that
|
|
@@ -1759,6 +1776,7 @@ function messageProviderFactory() {
|
|
|
1759
1776
|
return { ...ERROR_MESSAGES };
|
|
1760
1777
|
}
|
|
1761
1778
|
|
|
1779
|
+
const resolvedPromise = Promise.resolve();
|
|
1762
1780
|
class FsControlDirective {
|
|
1763
1781
|
wrapperSelector;
|
|
1764
1782
|
messageSelector;
|
|
@@ -1774,7 +1792,6 @@ class FsControlDirective {
|
|
|
1774
1792
|
};
|
|
1775
1793
|
}
|
|
1776
1794
|
errors = [];
|
|
1777
|
-
_control;
|
|
1778
1795
|
_elementRef = inject(ElementRef);
|
|
1779
1796
|
_renderer2 = inject(Renderer2);
|
|
1780
1797
|
_injector = inject(Injector);
|
|
@@ -1782,19 +1799,31 @@ class FsControlDirective {
|
|
|
1782
1799
|
_ngControl = inject(NgControl, { optional: true });
|
|
1783
1800
|
_formDirective = inject(FsFormDirective, { optional: true });
|
|
1784
1801
|
_destroy$ = new Subject();
|
|
1802
|
+
_statusChanges$ = new Subject();
|
|
1803
|
+
_boundControl;
|
|
1785
1804
|
constructor() {
|
|
1786
|
-
|
|
1787
|
-
if (_ngControl) {
|
|
1788
|
-
this._control = _ngControl.control;
|
|
1789
|
-
}
|
|
1790
|
-
else {
|
|
1805
|
+
if (!this._ngControl) {
|
|
1791
1806
|
console.error('The element does not have a valid ngModel', this._elementRef.nativeElement);
|
|
1792
1807
|
}
|
|
1793
1808
|
}
|
|
1809
|
+
/**
|
|
1810
|
+
* Always read the control through the directive rather than caching it.
|
|
1811
|
+
*
|
|
1812
|
+
* NgForm.addControl() runs in a microtask and reassigns dir.control to
|
|
1813
|
+
* whatever FormGroup.registerControl() returns — and registerControl() hands
|
|
1814
|
+
* back the EXISTING control when the name is already taken (two fields
|
|
1815
|
+
* sharing a name, or lazily created content re-registering). A control
|
|
1816
|
+
* captured in the constructor is then an orphan: it never emits again, so
|
|
1817
|
+
* statusChanges never fires and the validation message is never rendered.
|
|
1818
|
+
*/
|
|
1819
|
+
get _control() {
|
|
1820
|
+
return this._ngControl?.control;
|
|
1821
|
+
}
|
|
1794
1822
|
ngOnInit() {
|
|
1795
1823
|
this._setupValidators();
|
|
1796
1824
|
}
|
|
1797
1825
|
ngOnDestroy() {
|
|
1826
|
+
this._statusChanges$.complete();
|
|
1798
1827
|
this._destroy$.next(null);
|
|
1799
1828
|
this._destroy$.complete();
|
|
1800
1829
|
}
|
|
@@ -1936,11 +1965,30 @@ class FsControlDirective {
|
|
|
1936
1965
|
}
|
|
1937
1966
|
}
|
|
1938
1967
|
_subscribeToStatusChagnes() {
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1968
|
+
// Registration is deferred to a microtask by NgForm.addControl(), so wait
|
|
1969
|
+
// for it to settle before binding — otherwise we subscribe to the control
|
|
1970
|
+
// that is about to be replaced.
|
|
1971
|
+
resolvedPromise
|
|
1972
|
+
.then(() => this._bindStatusChanges());
|
|
1973
|
+
}
|
|
1974
|
+
_bindStatusChanges() {
|
|
1975
|
+
const control = this._control;
|
|
1976
|
+
if (!control || control === this._boundControl) {
|
|
1977
|
+
return;
|
|
1943
1978
|
}
|
|
1979
|
+
this._boundControl = control;
|
|
1980
|
+
this._statusChanges$.next();
|
|
1981
|
+
control.statusChanges
|
|
1982
|
+
.pipe(takeUntil(this._statusChanges$), takeUntil(this._destroy$))
|
|
1983
|
+
.subscribe(() => {
|
|
1984
|
+
// The control can still be swapped out from under us (a sibling with
|
|
1985
|
+
// the same name registering later), so re-bind before rendering.
|
|
1986
|
+
if (this._control !== this._boundControl) {
|
|
1987
|
+
this._bindStatusChanges();
|
|
1988
|
+
}
|
|
1989
|
+
this.render();
|
|
1990
|
+
});
|
|
1991
|
+
this.render();
|
|
1944
1992
|
}
|
|
1945
1993
|
_getWrapper(node, count = 0) {
|
|
1946
1994
|
if (!node || count > 10) {
|