@ng-modular-forms/core 0.3.0 → 0.4.0

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Ron Bodnar
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ron Bodnar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,125 +1,125 @@
1
- # @ng-modular-forms/core
2
-
3
- Core primitives for orchestrating complex Angular reactive forms.
4
-
5
- ## What This Provides
6
-
7
- - Form orchestration
8
- - Reactive logic isolation
9
- - Data mapping layer
10
-
11
- ## Installation
12
-
13
- ```bash
14
- npm install @ng-modular-forms/core
15
- ```
16
-
17
- ## Key Concepts
18
-
19
- ### FormOrchestratorBase
20
-
21
- Coordinates form structure and lifecycle.
22
-
23
- ```ts
24
- @Component({...})
25
- export class ExampleComponent extends FormOrchestratorBase {
26
- form = new FormGroup({});
27
-
28
- ngOnInit() {
29
- // Handlers are not required if there is no reactive logic or value change subscriptions.
30
- const mainHandler = inject(ExampleFormHandler);
31
- const sectionAHandler = inject(SectionAHandler);
32
-
33
- const formOptions = {
34
- mainHandler: mainHandler,
35
- subHandlers: {
36
- sectionA: sectionAHandler
37
- }
38
- };
39
-
40
- this.initialize(this.form, formOptions);
41
- }
42
-
43
- // Only required if forms are split across multiple components
44
- onSubformReady(form: FormGroup, key: string) {
45
- super.onSubformReady(form, key);
46
- }
47
- }
48
- ```
49
-
50
- ---
51
-
52
- ### FormHandlerBase
53
-
54
- Encapsulates reactive logic.
55
-
56
- ```ts
57
- const CONTROL_NAMES = ["fieldA", "dependentField"] as const;
58
-
59
- type ControlNames = (typeof CONTROL_NAMES)[number];
60
-
61
- @Injectable()
62
- export class SectionAHandler extends FormHandlerBase<ControlNames> {
63
- override getReactiveLogic(): Subscription {
64
- this.registerControls(this.form, [...CONTROL_NAMES]);
65
-
66
- return this.valueChangesOf("fieldA").subscribe((value) => {
67
- if (value) {
68
- this.controls.dependentField.enable();
69
- } else {
70
- this.controls.dependentField.disable();
71
- }
72
- });
73
- }
74
- }
75
- ```
76
-
77
- ---
78
-
79
- ### FormMapperBase
80
-
81
- Handles transformations between API and form.
82
-
83
- ```ts
84
- export class ExampleMapper extends FormMapperBase<ApiModel, RequestModel> {
85
- buildRequest(form: FormGroup) {
86
- return {
87
- fieldA: form.value.fieldA,
88
- fieldB: form.value.fieldB,
89
- };
90
- }
91
-
92
- transformFromModel(model: ApiModel) {
93
- return {
94
- fieldA: model.fieldA,
95
- fieldB: model.fieldB,
96
- };
97
- }
98
- }
99
- ```
100
-
101
- ---
102
-
103
- ### Responsibility Boundaries
104
-
105
- | Layer | Responsibility |
106
- | :--------------- | :------------------------------------------------------- |
107
- | **Orchestrator** | Manages form composition and lifecycle coordination. |
108
- | **Handler** | Encapsulates all reactive logic and stream management. |
109
- | **Mapper** | Handles data transformation between API and Form states. |
110
-
111
- ---
112
-
113
- ### No UI Included
114
-
115
- This package does **not** provide UI components.
116
-
117
- Use:
118
-
119
- - @ng-modular-forms/behavior
120
- - @ng-modular-forms/input
121
- - @ng-modular-forms/material
122
-
123
- ## License
124
-
125
- MIT
1
+ # @ng-modular-forms/core
2
+
3
+ Core primitives for orchestrating complex Angular reactive forms.
4
+
5
+ ## What This Provides
6
+
7
+ - Form orchestration
8
+ - Reactive logic isolation
9
+ - Data mapping layer
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @ng-modular-forms/core
15
+ ```
16
+
17
+ ## Key Concepts
18
+
19
+ ### FormOrchestratorBase
20
+
21
+ Coordinates form structure and lifecycle.
22
+
23
+ ```ts
24
+ @Component({...})
25
+ export class ExampleComponent extends FormOrchestratorBase {
26
+ form = new FormGroup({});
27
+
28
+ ngOnInit() {
29
+ // Handlers are not required if there is no reactive logic or value change subscriptions.
30
+ const mainHandler = inject(ExampleFormHandler);
31
+ const sectionAHandler = inject(SectionAHandler);
32
+
33
+ const formOptions = {
34
+ mainHandler: mainHandler,
35
+ subHandlers: {
36
+ sectionA: sectionAHandler
37
+ }
38
+ };
39
+
40
+ this.initialize(this.form, formOptions);
41
+ }
42
+
43
+ // Only required if forms are split across multiple components
44
+ onSubformReady(form: FormGroup, key: string) {
45
+ super.onSubformReady(form, key);
46
+ }
47
+ }
48
+ ```
49
+
50
+ ---
51
+
52
+ ### FormHandlerBase
53
+
54
+ Encapsulates reactive logic.
55
+
56
+ ```ts
57
+ const CONTROL_NAMES = ["fieldA", "dependentField"] as const;
58
+
59
+ type ControlNames = (typeof CONTROL_NAMES)[number];
60
+
61
+ @Injectable()
62
+ export class SectionAHandler extends FormHandlerBase<ControlNames> {
63
+ override getReactiveLogic(): Subscription {
64
+ this.registerControls(this.form, [...CONTROL_NAMES]);
65
+
66
+ return this.valueChangesOf("fieldA").subscribe((value) => {
67
+ if (value) {
68
+ this.controls.dependentField.enable();
69
+ } else {
70
+ this.controls.dependentField.disable();
71
+ }
72
+ });
73
+ }
74
+ }
75
+ ```
76
+
77
+ ---
78
+
79
+ ### FormMapperBase
80
+
81
+ Handles transformations between API and form.
82
+
83
+ ```ts
84
+ export class ExampleMapper extends FormMapperBase<ApiModel, RequestModel> {
85
+ buildRequest(form: FormGroup) {
86
+ return {
87
+ fieldA: form.value.fieldA,
88
+ fieldB: form.value.fieldB,
89
+ };
90
+ }
91
+
92
+ transformFromModel(model: ApiModel) {
93
+ return {
94
+ fieldA: model.fieldA,
95
+ fieldB: model.fieldB,
96
+ };
97
+ }
98
+ }
99
+ ```
100
+
101
+ ---
102
+
103
+ ### Responsibility Boundaries
104
+
105
+ | Layer | Responsibility |
106
+ | :--------------- | :------------------------------------------------------- |
107
+ | **Orchestrator** | Manages form composition and lifecycle coordination. |
108
+ | **Handler** | Encapsulates all reactive logic and stream management. |
109
+ | **Mapper** | Handles data transformation between API and Form states. |
110
+
111
+ ---
112
+
113
+ ### No UI Included
114
+
115
+ This package does **not** provide UI components.
116
+
117
+ Use:
118
+
119
+ - @ng-modular-forms/behavior
120
+ - @ng-modular-forms/input
121
+ - @ng-modular-forms/material
122
+
123
+ ## License
124
+
125
+ MIT
@@ -1,25 +1,44 @@
1
+ import { NgControl, Validators, FormGroup } from '@angular/forms';
1
2
  import * as i0 from '@angular/core';
2
- import { inject, ChangeDetectorRef, input, HostBinding, Optional, Self, Directive, signal, computed } from '@angular/core';
3
- import * as i1 from '@angular/forms';
4
- import { FormGroup, FormGroupDirective, Validators } from '@angular/forms';
3
+ import { inject, ChangeDetectorRef, DestroyRef, input, booleanAttribute, signal, computed, HostBinding, Directive } from '@angular/core';
5
4
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
6
5
  import { Subscription } from 'rxjs';
7
6
 
8
- /**
9
- * Base implementation for custom form controls that integrate with Angular Reactive Forms (ControlValueAccessor)
10
- *
11
- * NOTE: This class is UI-layer only and should not be used in form orchestration logic.
12
- */
13
- class FormControlValueAccessor {
14
- ngControl;
15
- elementRef;
7
+ class FormControlBase {
8
+ static nextId = 0;
9
+ id = `nmf-form-control-${FormControlBase.nextId++}`;
16
10
  cdr = inject(ChangeDetectorRef);
11
+ destroyRef = inject(DestroyRef);
12
+ ngControl = inject(NgControl, {
13
+ optional: true,
14
+ self: true,
15
+ });
16
+ label = input('');
17
+ classList = input([]);
18
+ loading = input(false);
17
19
  _name = input('', { alias: 'name' });
18
20
  _placeholder = input('', { alias: 'placeholder' });
19
- _required = input(false, { alias: 'required' });
20
- _disabled = input(false, { alias: 'disabled' });
21
+ _required = input(false, {
22
+ alias: 'required',
23
+ transform: booleanAttribute,
24
+ });
25
+ _disabledByInput = input(false, {
26
+ alias: 'disabled',
27
+ transform: booleanAttribute,
28
+ });
21
29
  _readonly = input(false, { alias: 'readonly' });
22
- formControlName = input('');
30
+ _disabledByCva = signal(false);
31
+ _disabled = computed(() => this._disabledByInput() || this._disabledByCva() || this.loading());
32
+ focused = false;
33
+ lastErrorState = false;
34
+ _value = null;
35
+ get value() {
36
+ return this._value;
37
+ }
38
+ set value(value) {
39
+ this._value = value;
40
+ this.cdr.markForCheck();
41
+ }
23
42
  get name() {
24
43
  return this._name();
25
44
  }
@@ -27,7 +46,9 @@ class FormControlValueAccessor {
27
46
  return this._placeholder();
28
47
  }
29
48
  get required() {
30
- return this._required();
49
+ const formControl = this.ngControl?.control;
50
+ const required = !!formControl && formControl.hasValidator(Validators.required);
51
+ return this._required() || required;
31
52
  }
32
53
  get disabled() {
33
54
  return this._disabled();
@@ -35,85 +56,33 @@ class FormControlValueAccessor {
35
56
  get readonly() {
36
57
  return this._readonly();
37
58
  }
38
- static nextId = 0;
39
- id = `nmf-form-control-${FormControlValueAccessor.nextId++}`;
40
- _value = null;
41
- get value() {
42
- return this._value;
43
- }
44
- set value(val) {
45
- this._value = val;
59
+ get errorState() {
60
+ const control = this.ngControl?.control;
61
+ return !!control && control.invalid && control.touched;
46
62
  }
47
- onChange = (_value) => { };
48
- onTouched = () => { };
49
- constructor(ngControl, elementRef) {
50
- this.ngControl = ngControl;
51
- this.elementRef = elementRef;
52
- if (this.ngControl != null) {
53
- this.ngControl.valueAccessor = this;
54
- }
63
+ get empty() {
64
+ return (this._value === null || this._value === '' || this._value === undefined);
55
65
  }
56
- writeValue(value) {
57
- this._value = value;
58
- this.cdr.markForCheck();
59
- }
60
- registerOnChange(fn) {
61
- this.onChange = fn;
62
- }
63
- registerOnTouched(fn) {
64
- this.onTouched = fn;
65
- }
66
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: FormControlValueAccessor, deps: [{ token: i1.NgControl, optional: true, self: true }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
67
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.21", type: FormControlValueAccessor, isStandalone: true, inputs: { _name: { classPropertyName: "_name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, _placeholder: { classPropertyName: "_placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, _required: { classPropertyName: "_required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, _disabled: { classPropertyName: "_disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, _readonly: { classPropertyName: "_readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, formControlName: { classPropertyName: "formControlName", publicName: "formControlName", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "this.id" } }, ngImport: i0 });
68
- }
69
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: FormControlValueAccessor, decorators: [{
70
- type: Directive
71
- }], ctorParameters: () => [{ type: i1.NgControl, decorators: [{
72
- type: Optional
73
- }, {
74
- type: Self
75
- }] }, { type: i0.ElementRef }], propDecorators: { id: [{
76
- type: HostBinding
77
- }] } });
78
-
79
- class FormControlBase extends FormControlValueAccessor {
80
- label = input('');
81
- classList = input([]);
82
- loading = input(false);
83
- _form = signal(new FormGroup({}));
84
- _controlName = signal('');
85
- form = this._form.asReadonly();
86
- controlName = this._controlName.asReadonly();
87
- control = computed(() => this._form().get(this._controlName()));
88
- parentFormGroup = inject(FormGroupDirective, {
89
- optional: true,
90
- host: true,
91
- });
92
66
  ngOnInit() {
93
- const controlName = this.formControlName() ?? 'default';
94
- const form = this.parentFormGroup?.form;
95
- if (!form) {
96
- throw new Error(`FormGroupDirective not found. Ensure component is used inside a form group`);
97
- }
98
- this._form.set(form);
99
- this._controlName.set(controlName);
100
- const control = form.get(controlName);
67
+ const control = this.ngControl?.control;
101
68
  if (!control) {
102
- throw new Error(`FormControl '${controlName}' not found in parent FormGroup`);
69
+ throw new Error(`FormControl ${this.id} not found`);
103
70
  }
104
71
  control.statusChanges
105
- .pipe(takeUntilDestroyed())
106
- .subscribe(() => this.cdr.markForCheck());
107
- }
108
- isRequired() {
109
- const control = this._form()?.get(this._controlName());
110
- return !!control && control.hasValidator(Validators.required);
72
+ .pipe(takeUntilDestroyed(this.destroyRef))
73
+ .subscribe((val) => this.cdr.markForCheck());
74
+ }
75
+ ngDoCheck() {
76
+ const newState = this.errorState;
77
+ if (newState !== this.lastErrorState) {
78
+ this.lastErrorState = newState;
79
+ this.cdr.markForCheck();
80
+ }
111
81
  }
112
- serverError = computed(() => this.form()?.errors?.['server']);
113
82
  getErrorMessage() {
114
- const control = this._form()?.get(this._controlName());
115
- if (!control || !control.errors || !control.touched)
116
- return '';
83
+ const control = this.ngControl?.control;
84
+ if (control == null || !control.errors || !control.touched)
85
+ return null;
117
86
  const firstKey = Object.keys(control.errors)[0];
118
87
  const error = control.errors[firstKey];
119
88
  switch (firstKey) {
@@ -131,18 +100,23 @@ class FormControlBase extends FormControlValueAccessor {
131
100
  return 'Invalid email address';
132
101
  case 'pattern':
133
102
  return 'Invalid format';
134
- case 'server':
135
- return error;
103
+ case 'custom':
104
+ if (typeof error === 'string') {
105
+ return error;
106
+ }
107
+ return 'Invalid value';
136
108
  default:
137
109
  return 'Invalid value';
138
110
  }
139
111
  }
140
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: FormControlBase, deps: null, target: i0.ɵɵFactoryTarget.Directive });
141
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.21", type: FormControlBase, isStandalone: true, inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, classList: { classPropertyName: "classList", publicName: "classList", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, usesInheritance: true, ngImport: i0 });
112
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: FormControlBase, deps: [], target: i0.ɵɵFactoryTarget.Directive });
113
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.21", type: FormControlBase, isStandalone: true, inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, classList: { classPropertyName: "classList", publicName: "classList", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, _name: { classPropertyName: "_name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, _placeholder: { classPropertyName: "_placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, _required: { classPropertyName: "_required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, _disabledByInput: { classPropertyName: "_disabledByInput", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, _readonly: { classPropertyName: "_readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "this.id" } }, ngImport: i0 });
142
114
  }
143
115
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: FormControlBase, decorators: [{
144
116
  type: Directive
145
- }] });
117
+ }], propDecorators: { id: [{
118
+ type: HostBinding
119
+ }] } });
146
120
 
147
121
  function getControl(controlName, form) {
148
122
  if (!form) {
@@ -166,19 +140,55 @@ function getControlValue(controlName, form) {
166
140
  if (typeof value === 'string') {
167
141
  const cleaned = value.replace(/,/g, '');
168
142
  if (!Number.isNaN(Number(cleaned))) {
169
- return parseCurrency(value);
143
+ return parseNumber(value);
170
144
  }
171
145
  }
172
146
  return value;
173
147
  }
174
- function parseCurrency(currencyString) {
175
- if (currencyString == null) {
176
- return 0;
148
+ function parseNumber(input) {
149
+ if (input == null || input === '')
150
+ return null;
151
+ if (typeof input === 'number') {
152
+ return Number.isFinite(input) ? input : null;
153
+ }
154
+ const normalized = input
155
+ .trim()
156
+ .replace(/,/g, '') // remove thousands
157
+ .replace(/(?!^)-/g, ''); // only allow leading '-'
158
+ if (normalized === '-' || normalized === '.' || normalized === '-.') {
159
+ return null;
160
+ }
161
+ const num = Number(normalized);
162
+ return Number.isFinite(num) ? num : null;
163
+ }
164
+ function formatNumber(value, locale = 'en-US', options = { maximumFractionDigits: 2 }) {
165
+ if (value == null || value === '') {
166
+ return null;
167
+ }
168
+ if (typeof value !== 'string') {
169
+ value = String(value);
170
+ }
171
+ value = value.replace(/[^\d\-,.]/g, '');
172
+ if (value === '') {
173
+ return null;
174
+ }
175
+ const isNegative = value.startsWith('-');
176
+ const numericValue = Number(value.replace(/[,$-]/g, ''));
177
+ const formatted = numericValue.toLocaleString(locale, options);
178
+ return isNegative ? `-${formatted}` : formatted;
179
+ }
180
+ function formatCurrency(value, locale = 'en-US', options = { maximumFractionDigits: 2 }) {
181
+ if (value == null || value === '') {
182
+ return null;
177
183
  }
178
- if (typeof currencyString === 'number') {
179
- return currencyString;
184
+ if (typeof value !== 'string') {
185
+ value = String(value);
180
186
  }
181
- return Number(currencyString.replace(/[^0-9.-]/g, ''));
187
+ value = value.replace(/[^\d\-,.]/g, '');
188
+ const isNegative = value.startsWith('-');
189
+ const numericValue = Number(value.replace(/[,$-]/g, ''));
190
+ const formatted = numericValue.toLocaleString(locale, options);
191
+ return isNegative ? `-${formatted}` : formatted;
182
192
  }
183
193
 
184
194
  class FormHandlerBase {
@@ -322,5 +332,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImpo
322
332
  * Generated bundle index. Do not edit.
323
333
  */
324
334
 
325
- export { FormControlBase, FormControlValueAccessor, FormHandlerBase, FormMapperBase, FormOrchestratorBase, getControl, getControlValue, parseCurrency };
335
+ export { FormControlBase, FormHandlerBase, FormMapperBase, FormOrchestratorBase, formatCurrency, formatNumber, getControl, getControlValue, parseNumber };
326
336
  //# sourceMappingURL=ng-modular-forms-core.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ng-modular-forms-core.mjs","sources":["../../../projects/core/src/lib/form-control-value-assessor.ts","../../../projects/core/src/lib/form-control-base.ts","../../../projects/core/src/lib/form.util.ts","../../../projects/core/src/lib/form-handler-base.ts","../../../projects/core/src/lib/form-mapper-base.ts","../../../projects/core/src/lib/form-orchestrator-base.ts","../../../projects/core/src/public-api.ts","../../../projects/core/src/ng-modular-forms-core.ts"],"sourcesContent":["import {\r\n Directive,\r\n ElementRef,\r\n HostBinding,\r\n Optional,\r\n Self,\r\n inject,\r\n ChangeDetectorRef,\r\n input,\r\n} from '@angular/core';\r\nimport { ControlValueAccessor, NgControl } from '@angular/forms';\r\n\r\n/**\r\n * Base implementation for custom form controls that integrate with Angular Reactive Forms (ControlValueAccessor)\r\n *\r\n * NOTE: This class is UI-layer only and should not be used in form orchestration logic.\r\n */\r\n\r\n@Directive()\r\nexport abstract class FormControlValueAccessor<\r\n T,\r\n> implements ControlValueAccessor {\r\n protected readonly cdr = inject(ChangeDetectorRef);\r\n\r\n protected readonly _name = input<string>('', { alias: 'name' });\r\n protected readonly _placeholder = input<string>('', { alias: 'placeholder' });\r\n protected readonly _required = input<boolean>(false, { alias: 'required' });\r\n protected readonly _disabled = input<boolean>(false, { alias: 'disabled' });\r\n protected readonly _readonly = input<boolean>(false, { alias: 'readonly' });\r\n\r\n protected readonly formControlName = input<string>('');\r\n\r\n get name(): string {\r\n return this._name();\r\n }\r\n\r\n get placeholder(): string {\r\n return this._placeholder();\r\n }\r\n\r\n get required(): boolean {\r\n return this._required();\r\n }\r\n\r\n get disabled(): boolean {\r\n return this._disabled();\r\n }\r\n\r\n get readonly(): boolean {\r\n return this._readonly();\r\n }\r\n\r\n static nextId = 0;\r\n\r\n @HostBinding()\r\n id = `nmf-form-control-${FormControlValueAccessor.nextId++}`;\r\n\r\n _value: T | null = null;\r\n\r\n get value(): T | null {\r\n return this._value;\r\n }\r\n\r\n set value(val: T | null) {\r\n this._value = val;\r\n }\r\n\r\n onChange = (_value: T) => {};\r\n onTouched = () => {};\r\n\r\n constructor(\r\n @Optional() @Self() public ngControl: NgControl,\r\n protected elementRef: ElementRef<HTMLElement>,\r\n ) {\r\n if (this.ngControl != null) {\r\n this.ngControl.valueAccessor = this;\r\n }\r\n }\r\n\r\n writeValue(value: T): void {\r\n this._value = value;\r\n this.cdr.markForCheck();\r\n }\r\n\r\n registerOnChange(fn: (value: T) => void): void {\r\n this.onChange = fn;\r\n }\r\n\r\n registerOnTouched(fn: () => void): void {\r\n this.onTouched = fn;\r\n }\r\n}\r\n","import {\r\n FormControl,\r\n FormGroup,\r\n FormGroupDirective,\r\n Validators,\r\n} from '@angular/forms';\r\nimport {\r\n computed,\r\n Directive,\r\n inject,\r\n input,\r\n OnInit,\r\n signal,\r\n} from '@angular/core';\r\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\r\nimport { FormControlValueAccessor } from './form-control-value-assessor';\r\n\r\n@Directive()\r\nexport abstract class FormControlBase<T>\r\n extends FormControlValueAccessor<T>\r\n implements OnInit\r\n{\r\n label = input<string>('');\r\n classList = input<string[]>([]);\r\n loading = input<boolean>(false);\r\n\r\n protected _form = signal<FormGroup>(new FormGroup({}));\r\n protected _controlName = signal<string>('');\r\n\r\n readonly form = this._form.asReadonly();\r\n readonly controlName = this._controlName.asReadonly();\r\n\r\n readonly control = computed(\r\n () => this._form().get(this._controlName()) as FormControl,\r\n );\r\n\r\n private parentFormGroup = inject(FormGroupDirective, {\r\n optional: true,\r\n host: true,\r\n });\r\n\r\n ngOnInit() {\r\n const controlName = this.formControlName() ?? 'default';\r\n const form = this.parentFormGroup?.form;\r\n\r\n if (!form) {\r\n throw new Error(\r\n `FormGroupDirective not found. Ensure component is used inside a form group`,\r\n );\r\n }\r\n\r\n this._form.set(form);\r\n this._controlName.set(controlName);\r\n\r\n const control = form.get(controlName);\r\n\r\n if (!control) {\r\n throw new Error(\r\n `FormControl '${controlName}' not found in parent FormGroup`,\r\n );\r\n }\r\n\r\n control.statusChanges\r\n .pipe(takeUntilDestroyed())\r\n .subscribe(() => this.cdr.markForCheck());\r\n }\r\n\r\n protected isRequired(): boolean {\r\n const control = this._form()?.get(this._controlName());\r\n return !!control && control.hasValidator(Validators.required);\r\n }\r\n\r\n protected serverError = computed(\r\n () => this.form()?.errors?.['server'] as string | undefined,\r\n );\r\n\r\n protected getErrorMessage(): string {\r\n const control = this._form()?.get(this._controlName());\r\n\r\n if (!control || !control.errors || !control.touched) return '';\r\n\r\n const firstKey = Object.keys(control.errors)[0];\r\n const error = control.errors[firstKey];\r\n\r\n switch (firstKey) {\r\n case 'required':\r\n return 'This field is required';\r\n case 'minlength':\r\n return `Minimum length is ${error.requiredLength}`;\r\n case 'maxlength':\r\n return `Maximum length is ${error.requiredLength}`;\r\n case 'min':\r\n return `Minimum value is ${error.min}`;\r\n case 'max':\r\n return `Maximum value is ${error.max}`;\r\n case 'email':\r\n return 'Invalid email address';\r\n case 'pattern':\r\n return 'Invalid format';\r\n case 'server':\r\n return error;\r\n default:\r\n return 'Invalid value';\r\n }\r\n }\r\n}\r\n","import { FormControl, FormGroup } from '@angular/forms';\r\n\r\nexport function getControl<T = unknown>(controlName: string, form: FormGroup) {\r\n if (!form) {\r\n throw new Error(\r\n `Missing form instance while getting the control of \"${controlName}\"`,\r\n );\r\n }\r\n\r\n const control = form.get(controlName) as FormControl<T>;\r\n if (!control) {\r\n throw new Error(`Missing control \"${controlName}\" in form \"${form}\"`);\r\n }\r\n\r\n return control;\r\n}\r\n\r\nexport function getControlValue<T = unknown>(\r\n controlName: string,\r\n form: FormGroup,\r\n): T | null;\r\n\r\nexport function getControlValue(\r\n controlName: string,\r\n form: FormGroup,\r\n): number | null;\r\n\r\nexport function getControlValue<T = unknown>(\r\n controlName: string,\r\n form: FormGroup,\r\n): T | null {\r\n const control = getControl<T>(controlName, form);\r\n if (!control) {\r\n throw new Error(`Missing control \"${controlName}\" in form \"${form}\"`);\r\n }\r\n\r\n const value = control.getRawValue();\r\n if (value === '') {\r\n return null;\r\n }\r\n\r\n if (typeof value === 'string') {\r\n const cleaned = value.replace(/,/g, '');\r\n\r\n if (!Number.isNaN(Number(cleaned))) {\r\n return parseCurrency(value) as T;\r\n }\r\n }\r\n\r\n return value;\r\n}\r\n\r\nexport function parseCurrency(\r\n currencyString: string | number | null | undefined,\r\n): number {\r\n if (currencyString == null) {\r\n return 0;\r\n }\r\n\r\n if (typeof currencyString === 'number') {\r\n return currencyString;\r\n }\r\n\r\n return Number(currencyString.replace(/[^0-9.-]/g, ''));\r\n}\r\n","import { FormControl, FormGroup } from '@angular/forms';\r\nimport { Observable, Subscription } from 'rxjs';\r\nimport { getControl } from './form.util';\r\n\r\nexport abstract class FormHandlerBase<ControlNames extends string = string> {\r\n abstract getReactiveLogic(form?: FormGroup): Subscription;\r\n\r\n protected registeredControls: Record<string, FormControl> = {};\r\n\r\n /**\r\n * Registers form controls for later reactive access.\r\n */\r\n registerControls(form: FormGroup, controlNames: ControlNames[]): void {\r\n controlNames.forEach((cn) => {\r\n const control = getControl(cn.replace(/_/g, '.'), form);\r\n\r\n if (!control) {\r\n throw new Error(\r\n `Failed to register control with name: \"${cn}\" in form group: \"${form}\"`,\r\n );\r\n }\r\n\r\n const key = cn as ControlNames;\r\n this.registeredControls[key] = control;\r\n });\r\n }\r\n\r\n valueChangesOf<T>(key: ControlNames): Observable<T> {\r\n if (!this.registeredControls[key]) {\r\n throw new Error(\r\n `Control with name: \"${key}\" not found. Ensure it is registered in registerControls(...)`,\r\n );\r\n }\r\n\r\n return this.registeredControls[key].valueChanges as Observable<T>;\r\n }\r\n}\r\n","import { FormGroup } from '@angular/forms';\r\n\r\nexport abstract class FormMapperBase<\r\n TModelIn = unknown,\r\n TModelOut = unknown,\r\n TFormModel = unknown,\r\n TStore = Record<string, unknown>,\r\n> {\r\n /**\r\n * Maps form state + external store into an API request payload.\r\n */\r\n abstract buildRequest(form: FormGroup, store: TStore): Partial<TModelOut>;\r\n\r\n /**\r\n * Transforms a domain/API model into a form-compatible structure.\r\n */\r\n abstract transformFromModel(model: TModelIn): TFormModel;\r\n\r\n patchForm(\r\n form: FormGroup,\r\n data: TFormModel,\r\n _context: TStore,\r\n emitEvent: boolean = false,\r\n ): void {\r\n form.patchValue(\r\n {\r\n ...(data as Record<string, unknown>),\r\n },\r\n { emitEvent },\r\n );\r\n }\r\n\r\n patchFromModel(form: FormGroup, model: TModelIn, store: TStore): void {\r\n const formModel = this.transformFromModel(model);\r\n this.patchForm(form, formModel, store);\r\n }\r\n}\r\n","import { FormGroup } from '@angular/forms';\r\nimport { Directive, OnDestroy, signal } from '@angular/core';\r\nimport { Subscription } from 'rxjs';\r\nimport { FormHandlerBase } from './form-handler-base';\r\n\r\nexport type FormStatus = 'idle' | 'submitting' | 'error' | 'success';\r\n\r\ntype FormHandlerRegistry = Record<string, FormHandlerBase<string>>;\r\n\r\ninterface FormOrchestratorOptions {\r\n mainHandler?: FormHandlerBase<string> | null;\r\n subHandlers?: FormHandlerRegistry;\r\n}\r\n\r\n@Directive()\r\nexport abstract class FormOrchestratorBase implements OnDestroy {\r\n private _form = signal<FormGroup>(new FormGroup({}));\r\n\r\n private _mainHandler = signal<FormHandlerBase<string> | null>(null);\r\n private _subHandlers = signal<FormHandlerRegistry>({});\r\n\r\n private _status = signal<FormStatus>('idle');\r\n private _errorMessage = signal<string | null>(null);\r\n\r\n private _loadedHandlers = signal<number>(0);\r\n private _allHandlersLoaded = signal<boolean>(false);\r\n\r\n private _logicSubscription = new Subscription();\r\n\r\n public readonly form = this._form.asReadonly();\r\n public readonly mainHandler = this._mainHandler.asReadonly();\r\n public readonly subHandlers = this._subHandlers.asReadonly();\r\n public readonly status = this._status.asReadonly();\r\n public readonly errorMessage = this._errorMessage.asReadonly();\r\n\r\n /**\r\n * Initializes orchestration state.\r\n * Must be called before any subform registration or handler execution.\r\n */\r\n public initialize(\r\n form: FormGroup,\r\n formOrchestratorOptions?: FormOrchestratorOptions,\r\n ) {\r\n const { mainHandler, subHandlers = {} } = formOrchestratorOptions ?? {};\r\n\r\n this._form.set(form);\r\n this._mainHandler.set(mainHandler ?? null);\r\n this._subHandlers.set(subHandlers);\r\n this._loadedHandlers.set(0);\r\n\r\n const subHandlerCount = Object.keys(this.subHandlers());\r\n if (subHandlerCount.length === 0) {\r\n this.loadMainHandler();\r\n }\r\n }\r\n\r\n public setForm(form: FormGroup) {\r\n this._form.set(form);\r\n }\r\n\r\n public getHandler(key: string): FormHandlerBase<string> | undefined {\r\n return this.subHandlers()[key];\r\n }\r\n\r\n public setHandler(key: string, handler: FormHandlerBase<string>) {\r\n this._subHandlers.set({\r\n ...this._subHandlers(),\r\n [key]: handler,\r\n });\r\n }\r\n\r\n public addReactiveLogic(subscription: Subscription) {\r\n this._logicSubscription.add(subscription);\r\n }\r\n\r\n public setStatus(status: FormStatus) {\r\n this._status.set(status);\r\n }\r\n\r\n public setErrorMessage(message: string | null) {\r\n this._errorMessage.set(message);\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this._logicSubscription.unsubscribe();\r\n }\r\n\r\n /**\r\n * Registers a subform into the main form tree and coordinates handler execution.\r\n *\r\n * IMPORTANT:\r\n * - Handler execution is gated until all registered subhandlers are ready.\r\n * - Calling order matters; this is lifecycle-sensitive orchestration logic.\r\n */\r\n onSubformReady(\r\n subform: FormGroup,\r\n groupName: string,\r\n nestGroups: boolean = false,\r\n ): void {\r\n if (nestGroups) {\r\n this.form().setControl(groupName, subform);\r\n } else {\r\n const keys = Object.keys(subform.controls);\r\n keys.forEach((key) => {\r\n this.form().setControl(key, subform.get(key));\r\n });\r\n }\r\n\r\n // Prevent duplicate main handler execution when all subhandlers already resolved\r\n if (this._allHandlersLoaded()) {\r\n return;\r\n }\r\n\r\n const subformHandler = this.subHandlers()[groupName];\r\n\r\n if (subformHandler) {\r\n this._loadedHandlers.set(this._loadedHandlers() + 1);\r\n this.addReactiveLogic(subformHandler.getReactiveLogic());\r\n }\r\n\r\n const totalSubHandlers = Object.keys(this.subHandlers()).length;\r\n const allSubHandlersLoaded = this._loadedHandlers() === totalSubHandlers;\r\n\r\n if (allSubHandlersLoaded && this.mainHandler()) {\r\n this.loadMainHandler();\r\n }\r\n }\r\n\r\n private loadMainHandler() {\r\n if (!this.mainHandler()) return;\r\n\r\n this.addReactiveLogic(this.mainHandler()!.getReactiveLogic(this.form()));\r\n this._allHandlersLoaded.set(true);\r\n }\r\n}\r\n","/*\r\n * Public API Surface of core\r\n */\r\n\r\nexport * from './lib/form-control-value-assessor';\r\nexport * from './lib/form-control-base';\r\nexport * from './lib/form-handler-base';\r\nexport * from './lib/form-mapper-base';\r\nexport * from './lib/form-orchestrator-base';\r\nexport * from './lib/form.util';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ng-modular-forms-core.mjs","sources":["../../../projects/core/src/lib/form-control-base.ts","../../../projects/core/src/lib/form.util.ts","../../../projects/core/src/lib/form-handler-base.ts","../../../projects/core/src/lib/form-mapper-base.ts","../../../projects/core/src/lib/form-orchestrator-base.ts","../../../projects/core/src/public-api.ts","../../../projects/core/src/ng-modular-forms-core.ts"],"sourcesContent":["import { NgControl, Validators } from '@angular/forms';\nimport {\n booleanAttribute,\n ChangeDetectorRef,\n computed,\n DestroyRef,\n Directive,\n DoCheck,\n HostBinding,\n inject,\n input,\n OnInit,\n signal,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\n@Directive()\nexport abstract class FormControlBase<T> implements OnInit, DoCheck {\n static nextId = 0;\n\n @HostBinding()\n id = `nmf-form-control-${FormControlBase.nextId++}`;\n\n protected readonly cdr = inject(ChangeDetectorRef);\n protected readonly destroyRef = inject(DestroyRef);\n\n readonly ngControl = inject(NgControl, {\n optional: true,\n self: true,\n });\n\n readonly label = input<string>('');\n readonly classList = input<string[]>([]);\n readonly loading = input<boolean>(false);\n\n readonly _name = input<string>('', { alias: 'name' });\n readonly _placeholder = input<string>('', { alias: 'placeholder' });\n readonly _required = input<boolean, unknown>(false, {\n alias: 'required',\n transform: booleanAttribute,\n });\n readonly _disabledByInput = input<boolean, unknown>(false, {\n alias: 'disabled',\n transform: booleanAttribute,\n });\n readonly _readonly = input<boolean>(false, { alias: 'readonly' });\n\n protected readonly _disabledByCva = signal(false);\n readonly _disabled = computed(\n () => this._disabledByInput() || this._disabledByCva() || this.loading(),\n );\n\n focused = false;\n\n private lastErrorState = false;\n\n private _value: T | null = null;\n\n get value(): T | null {\n return this._value;\n }\n\n set value(value: T | null) {\n this._value = value;\n this.cdr.markForCheck();\n }\n\n get name(): string {\n return this._name();\n }\n\n get placeholder(): string {\n return this._placeholder();\n }\n\n get required(): boolean {\n const formControl = this.ngControl?.control;\n const required =\n !!formControl && formControl.hasValidator(Validators.required);\n return this._required() || required;\n }\n\n get disabled(): boolean {\n return this._disabled();\n }\n\n get readonly(): boolean {\n return this._readonly();\n }\n\n get errorState(): boolean {\n const control = this.ngControl?.control;\n return !!control && control.invalid && control.touched;\n }\n\n get empty(): boolean {\n return (\n this._value === null || this._value === '' || this._value === undefined\n );\n }\n\n ngOnInit() {\n const control = this.ngControl?.control;\n if (!control) {\n throw new Error(`FormControl ${this.id} not found`);\n }\n\n control.statusChanges\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe((val) => this.cdr.markForCheck());\n }\n\n ngDoCheck() {\n const newState = this.errorState;\n\n if (newState !== this.lastErrorState) {\n this.lastErrorState = newState;\n this.cdr.markForCheck();\n }\n }\n\n protected getErrorMessage(): string | null {\n const control = this.ngControl?.control;\n\n if (control == null || !control.errors || !control.touched) return null;\n\n const firstKey = Object.keys(control.errors)[0];\n const error = control.errors[firstKey];\n\n switch (firstKey) {\n case 'required':\n return 'This field is required';\n case 'minlength':\n return `Minimum length is ${error.requiredLength}`;\n case 'maxlength':\n return `Maximum length is ${error.requiredLength}`;\n case 'min':\n return `Minimum value is ${error.min}`;\n case 'max':\n return `Maximum value is ${error.max}`;\n case 'email':\n return 'Invalid email address';\n case 'pattern':\n return 'Invalid format';\n case 'custom':\n if (typeof error === 'string') {\n return error;\n }\n return 'Invalid value';\n default:\n return 'Invalid value';\n }\n }\n}\n","import { FormControl, FormGroup } from '@angular/forms';\n\nexport function getControl<T = unknown>(controlName: string, form: FormGroup) {\n if (!form) {\n throw new Error(\n `Missing form instance while getting the control of \"${controlName}\"`,\n );\n }\n\n const control = form.get(controlName) as FormControl<T>;\n if (!control) {\n throw new Error(`Missing control \"${controlName}\" in form \"${form}\"`);\n }\n\n return control;\n}\n\nexport function getControlValue<T = unknown>(\n controlName: string,\n form: FormGroup,\n): T | null;\n\nexport function getControlValue(\n controlName: string,\n form: FormGroup,\n): number | null;\n\nexport function getControlValue<T = unknown>(\n controlName: string,\n form: FormGroup,\n): T | null {\n const control = getControl<T>(controlName, form);\n if (!control) {\n throw new Error(`Missing control \"${controlName}\" in form \"${form}\"`);\n }\n\n const value = control.getRawValue();\n if (value === '') {\n return null;\n }\n\n if (typeof value === 'string') {\n const cleaned = value.replace(/,/g, '');\n\n if (!Number.isNaN(Number(cleaned))) {\n return parseNumber(value) as T;\n }\n }\n\n return value;\n}\n\nexport function parseNumber(\n input: string | number | null | undefined,\n): number | null {\n if (input == null || input === '') return null;\n\n if (typeof input === 'number') {\n return Number.isFinite(input) ? input : null;\n }\n\n const normalized = input\n .trim()\n .replace(/,/g, '') // remove thousands\n .replace(/(?!^)-/g, ''); // only allow leading '-'\n\n if (normalized === '-' || normalized === '.' || normalized === '-.') {\n return null;\n }\n\n const num = Number(normalized);\n\n return Number.isFinite(num) ? num : null;\n}\n\nexport function formatNumber(\n value: string | number | null,\n locale: string = 'en-US',\n options: Intl.NumberFormatOptions = { maximumFractionDigits: 2 },\n): string | null {\n if (value == null || value === '') {\n return null;\n }\n if (typeof value !== 'string') {\n value = String(value);\n }\n\n value = value.replace(/[^\\d\\-,.]/g, '');\n\n if (value === '') {\n return null;\n }\n\n const isNegative = value.startsWith('-');\n const numericValue = Number(value.replace(/[,$-]/g, ''));\n const formatted = numericValue.toLocaleString(locale, options);\n\n return isNegative ? `-${formatted}` : formatted;\n}\n\nexport function formatCurrency(\n value: string | number | null,\n locale: string = 'en-US',\n options: Intl.NumberFormatOptions = { maximumFractionDigits: 2 },\n): string | null {\n if (value == null || value === '') {\n return null;\n }\n if (typeof value !== 'string') {\n value = String(value);\n }\n\n value = value.replace(/[^\\d\\-,.]/g, '');\n\n const isNegative = value.startsWith('-');\n const numericValue = Number(value.replace(/[,$-]/g, ''));\n const formatted = numericValue.toLocaleString(locale, options);\n\n return isNegative ? `-${formatted}` : formatted;\n}\n","import { FormControl, FormGroup } from '@angular/forms';\nimport { Observable, Subscription } from 'rxjs';\nimport { getControl } from './form.util';\n\nexport abstract class FormHandlerBase<ControlNames extends string = string> {\n abstract getReactiveLogic(form?: FormGroup): Subscription;\n\n protected registeredControls: Record<string, FormControl> = {};\n\n /**\n * Registers form controls for later reactive access.\n */\n registerControls(form: FormGroup, controlNames: ControlNames[]): void {\n controlNames.forEach((cn) => {\n const control = getControl(cn.replace(/_/g, '.'), form);\n\n if (!control) {\n throw new Error(\n `Failed to register control with name: \"${cn}\" in form group: \"${form}\"`,\n );\n }\n\n const key = cn as ControlNames;\n this.registeredControls[key] = control;\n });\n }\n\n valueChangesOf<T>(key: ControlNames): Observable<T> {\n if (!this.registeredControls[key]) {\n throw new Error(\n `Control with name: \"${key}\" not found. Ensure it is registered in registerControls(...)`,\n );\n }\n\n return this.registeredControls[key].valueChanges as Observable<T>;\n }\n}\n","import { FormGroup } from '@angular/forms';\n\nexport abstract class FormMapperBase<\n TModelIn = unknown,\n TModelOut = unknown,\n TFormModel = unknown,\n TStore = Record<string, unknown>,\n> {\n /**\n * Maps form state + external store into an API request payload.\n */\n abstract buildRequest(form: FormGroup, store: TStore): Partial<TModelOut>;\n\n /**\n * Transforms a domain/API model into a form-compatible structure.\n */\n abstract transformFromModel(model: TModelIn): TFormModel;\n\n patchForm(\n form: FormGroup,\n data: TFormModel,\n _context: TStore,\n emitEvent: boolean = false,\n ): void {\n form.patchValue(\n {\n ...(data as Record<string, unknown>),\n },\n { emitEvent },\n );\n }\n\n patchFromModel(form: FormGroup, model: TModelIn, store: TStore): void {\n const formModel = this.transformFromModel(model);\n this.patchForm(form, formModel, store);\n }\n}\n","import { FormGroup } from '@angular/forms';\nimport { Directive, OnDestroy, signal } from '@angular/core';\nimport { Subscription } from 'rxjs';\nimport { FormHandlerBase } from './form-handler-base';\n\nexport type FormStatus = 'idle' | 'submitting' | 'error' | 'success';\n\ntype FormHandlerRegistry = Record<string, FormHandlerBase<string>>;\n\ninterface FormOrchestratorOptions {\n mainHandler?: FormHandlerBase<string> | null;\n subHandlers?: FormHandlerRegistry;\n}\n\n@Directive()\nexport abstract class FormOrchestratorBase implements OnDestroy {\n private _form = signal<FormGroup>(new FormGroup({}));\n\n private _mainHandler = signal<FormHandlerBase<string> | null>(null);\n private _subHandlers = signal<FormHandlerRegistry>({});\n\n private _status = signal<FormStatus>('idle');\n private _errorMessage = signal<string | null>(null);\n\n private _loadedHandlers = signal<number>(0);\n private _allHandlersLoaded = signal<boolean>(false);\n\n private _logicSubscription = new Subscription();\n\n public readonly form = this._form.asReadonly();\n public readonly mainHandler = this._mainHandler.asReadonly();\n public readonly subHandlers = this._subHandlers.asReadonly();\n public readonly status = this._status.asReadonly();\n public readonly errorMessage = this._errorMessage.asReadonly();\n\n /**\n * Initializes orchestration state.\n * Must be called before any subform registration or handler execution.\n */\n public initialize(\n form: FormGroup,\n formOrchestratorOptions?: FormOrchestratorOptions,\n ) {\n const { mainHandler, subHandlers = {} } = formOrchestratorOptions ?? {};\n\n this._form.set(form);\n this._mainHandler.set(mainHandler ?? null);\n this._subHandlers.set(subHandlers);\n this._loadedHandlers.set(0);\n\n const subHandlerCount = Object.keys(this.subHandlers());\n if (subHandlerCount.length === 0) {\n this.loadMainHandler();\n }\n }\n\n public setForm(form: FormGroup) {\n this._form.set(form);\n }\n\n public getHandler(key: string): FormHandlerBase<string> | undefined {\n return this.subHandlers()[key];\n }\n\n public setHandler(key: string, handler: FormHandlerBase<string>) {\n this._subHandlers.set({\n ...this._subHandlers(),\n [key]: handler,\n });\n }\n\n public addReactiveLogic(subscription: Subscription) {\n this._logicSubscription.add(subscription);\n }\n\n public setStatus(status: FormStatus) {\n this._status.set(status);\n }\n\n public setErrorMessage(message: string | null) {\n this._errorMessage.set(message);\n }\n\n ngOnDestroy(): void {\n this._logicSubscription.unsubscribe();\n }\n\n /**\n * Registers a subform into the main form tree and coordinates handler execution.\n *\n * IMPORTANT:\n * - Handler execution is gated until all registered subhandlers are ready.\n * - Calling order matters; this is lifecycle-sensitive orchestration logic.\n */\n onSubformReady(\n subform: FormGroup,\n groupName: string,\n nestGroups: boolean = false,\n ): void {\n if (nestGroups) {\n this.form().setControl(groupName, subform);\n } else {\n const keys = Object.keys(subform.controls);\n keys.forEach((key) => {\n this.form().setControl(key, subform.get(key));\n });\n }\n\n // Prevent duplicate main handler execution when all subhandlers already resolved\n if (this._allHandlersLoaded()) {\n return;\n }\n\n const subformHandler = this.subHandlers()[groupName];\n\n if (subformHandler) {\n this._loadedHandlers.set(this._loadedHandlers() + 1);\n this.addReactiveLogic(subformHandler.getReactiveLogic());\n }\n\n const totalSubHandlers = Object.keys(this.subHandlers()).length;\n const allSubHandlersLoaded = this._loadedHandlers() === totalSubHandlers;\n\n if (allSubHandlersLoaded && this.mainHandler()) {\n this.loadMainHandler();\n }\n }\n\n private loadMainHandler() {\n if (!this.mainHandler()) return;\n\n this.addReactiveLogic(this.mainHandler()!.getReactiveLogic(this.form()));\n this._allHandlersLoaded.set(true);\n }\n}\n","/*\n * Public API Surface of core\n */\n\nexport * from './lib/form-control-base';\nexport * from './lib/form-handler-base';\nexport * from './lib/form-mapper-base';\nexport * from './lib/form-orchestrator-base';\nexport * from './lib/form.util';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,21 +1,37 @@
1
- import { FormControl, FormGroup } from '@angular/forms';
2
- import { OnInit } from '@angular/core';
3
- import { FormControlValueAccessor } from './form-control-value-assessor';
1
+ import { NgControl } from '@angular/forms';
2
+ import { ChangeDetectorRef, DestroyRef, DoCheck, OnInit } from '@angular/core';
4
3
  import * as i0 from "@angular/core";
5
- export declare abstract class FormControlBase<T> extends FormControlValueAccessor<T> implements OnInit {
6
- label: import("@angular/core").InputSignal<string>;
7
- classList: import("@angular/core").InputSignal<string[]>;
8
- loading: import("@angular/core").InputSignal<boolean>;
9
- protected _form: import("@angular/core").WritableSignal<FormGroup<any>>;
10
- protected _controlName: import("@angular/core").WritableSignal<string>;
11
- readonly form: import("@angular/core").Signal<FormGroup<any>>;
12
- readonly controlName: import("@angular/core").Signal<string>;
13
- readonly control: import("@angular/core").Signal<FormControl<any>>;
14
- private parentFormGroup;
4
+ export declare abstract class FormControlBase<T> implements OnInit, DoCheck {
5
+ static nextId: number;
6
+ id: string;
7
+ protected readonly cdr: ChangeDetectorRef;
8
+ protected readonly destroyRef: DestroyRef;
9
+ readonly ngControl: NgControl | null;
10
+ readonly label: import("@angular/core").InputSignal<string>;
11
+ readonly classList: import("@angular/core").InputSignal<string[]>;
12
+ readonly loading: import("@angular/core").InputSignal<boolean>;
13
+ readonly _name: import("@angular/core").InputSignal<string>;
14
+ readonly _placeholder: import("@angular/core").InputSignal<string>;
15
+ readonly _required: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
16
+ readonly _disabledByInput: import("@angular/core").InputSignalWithTransform<boolean, unknown>;
17
+ readonly _readonly: import("@angular/core").InputSignal<boolean>;
18
+ protected readonly _disabledByCva: import("@angular/core").WritableSignal<boolean>;
19
+ readonly _disabled: import("@angular/core").Signal<boolean>;
20
+ focused: boolean;
21
+ private lastErrorState;
22
+ private _value;
23
+ get value(): T | null;
24
+ set value(value: T | null);
25
+ get name(): string;
26
+ get placeholder(): string;
27
+ get required(): boolean;
28
+ get disabled(): boolean;
29
+ get readonly(): boolean;
30
+ get errorState(): boolean;
31
+ get empty(): boolean;
15
32
  ngOnInit(): void;
16
- protected isRequired(): boolean;
17
- protected serverError: import("@angular/core").Signal<string | undefined>;
18
- protected getErrorMessage(): string;
33
+ ngDoCheck(): void;
34
+ protected getErrorMessage(): string | null;
19
35
  static ɵfac: i0.ɵɵFactoryDeclaration<FormControlBase<any>, never>;
20
- static ɵdir: i0.ɵɵDirectiveDeclaration<FormControlBase<any>, never, never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "classList": { "alias": "classList"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
36
+ static ɵdir: i0.ɵɵDirectiveDeclaration<FormControlBase<any>, never, never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "classList": { "alias": "classList"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "_name": { "alias": "name"; "required": false; "isSignal": true; }; "_placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "_required": { "alias": "required"; "required": false; "isSignal": true; }; "_disabledByInput": { "alias": "disabled"; "required": false; "isSignal": true; }; "_readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
21
37
  }
@@ -2,4 +2,6 @@ import { FormControl, FormGroup } from '@angular/forms';
2
2
  export declare function getControl<T = unknown>(controlName: string, form: FormGroup): FormControl<T>;
3
3
  export declare function getControlValue<T = unknown>(controlName: string, form: FormGroup): T | null;
4
4
  export declare function getControlValue(controlName: string, form: FormGroup): number | null;
5
- export declare function parseCurrency(currencyString: string | number | null | undefined): number;
5
+ export declare function parseNumber(input: string | number | null | undefined): number | null;
6
+ export declare function formatNumber(value: string | number | null, locale?: string, options?: Intl.NumberFormatOptions): string | null;
7
+ export declare function formatCurrency(value: string | number | null, locale?: string, options?: Intl.NumberFormatOptions): string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-modular-forms/core",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "peerDependencies": {
5
5
  "@angular/core": ">=19.0.0 <22.0.0",
6
6
  "@angular/common": ">=19.0.0 <22.0.0",
package/public-api.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- export * from './lib/form-control-value-assessor';
2
1
  export * from './lib/form-control-base';
3
2
  export * from './lib/form-handler-base';
4
3
  export * from './lib/form-mapper-base';
@@ -1,37 +0,0 @@
1
- import { ElementRef, ChangeDetectorRef } from '@angular/core';
2
- import { ControlValueAccessor, NgControl } from '@angular/forms';
3
- import * as i0 from "@angular/core";
4
- /**
5
- * Base implementation for custom form controls that integrate with Angular Reactive Forms (ControlValueAccessor)
6
- *
7
- * NOTE: This class is UI-layer only and should not be used in form orchestration logic.
8
- */
9
- export declare abstract class FormControlValueAccessor<T> implements ControlValueAccessor {
10
- ngControl: NgControl;
11
- protected elementRef: ElementRef<HTMLElement>;
12
- protected readonly cdr: ChangeDetectorRef;
13
- protected readonly _name: import("@angular/core").InputSignal<string>;
14
- protected readonly _placeholder: import("@angular/core").InputSignal<string>;
15
- protected readonly _required: import("@angular/core").InputSignal<boolean>;
16
- protected readonly _disabled: import("@angular/core").InputSignal<boolean>;
17
- protected readonly _readonly: import("@angular/core").InputSignal<boolean>;
18
- protected readonly formControlName: import("@angular/core").InputSignal<string>;
19
- get name(): string;
20
- get placeholder(): string;
21
- get required(): boolean;
22
- get disabled(): boolean;
23
- get readonly(): boolean;
24
- static nextId: number;
25
- id: string;
26
- _value: T | null;
27
- get value(): T | null;
28
- set value(val: T | null);
29
- onChange: (_value: T) => void;
30
- onTouched: () => void;
31
- constructor(ngControl: NgControl, elementRef: ElementRef<HTMLElement>);
32
- writeValue(value: T): void;
33
- registerOnChange(fn: (value: T) => void): void;
34
- registerOnTouched(fn: () => void): void;
35
- static ɵfac: i0.ɵɵFactoryDeclaration<FormControlValueAccessor<any>, [{ optional: true; self: true; }, null]>;
36
- static ɵdir: i0.ɵɵDirectiveDeclaration<FormControlValueAccessor<any>, never, never, { "_name": { "alias": "name"; "required": false; "isSignal": true; }; "_placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "_required": { "alias": "required"; "required": false; "isSignal": true; }; "_disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "_readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "formControlName": { "alias": "formControlName"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
37
- }