@ng-modular-forms/core 0.5.1 → 0.6.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/README.md +105 -39
- package/fesm2022/ng-modular-forms-core.mjs +965 -14
- package/fesm2022/ng-modular-forms-core.mjs.map +1 -1
- package/lib/behavior/currency.behavior.d.ts +3 -0
- package/lib/behavior/text.behavior.d.ts +5 -0
- package/lib/input/input-currency.component.d.ts +13 -0
- package/lib/input/input-datepicker.component.d.ts +13 -0
- package/lib/input/input-number.component.d.ts +16 -0
- package/lib/input/input-select.component.d.ts +14 -0
- package/lib/input/input-text.component.d.ts +10 -0
- package/lib/input/input-textarea.component.d.ts +8 -0
- package/lib/input/input-timepicker.component.d.ts +12 -0
- package/lib/types.d.ts +2 -2
- package/package.json +23 -1
- package/public-api.d.ts +9 -0
package/README.md
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
# @ng-modular-forms/core
|
|
2
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
|
|
3
|
+
Core primitives, behaviors, and input components for orchestrating complex Angular reactive forms.
|
|
10
4
|
|
|
11
5
|
## Installation
|
|
12
6
|
|
|
@@ -14,31 +8,38 @@ Core primitives for orchestrating complex Angular reactive forms.
|
|
|
14
8
|
npm install @ng-modular-forms/core
|
|
15
9
|
```
|
|
16
10
|
|
|
11
|
+
There is also an optional package which supports Angular Material UI components:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @ng-modular-forms/material
|
|
15
|
+
```
|
|
16
|
+
|
|
17
17
|
## Key Concepts
|
|
18
18
|
|
|
19
|
-
###
|
|
19
|
+
### FormOrchestrator
|
|
20
20
|
|
|
21
21
|
Coordinates form structure and lifecycle.
|
|
22
22
|
|
|
23
23
|
```ts
|
|
24
24
|
@Component({...})
|
|
25
|
-
export class ExampleComponent extends
|
|
25
|
+
export class ExampleComponent extends FormOrchestrator {
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
constructor(
|
|
28
|
+
override readonly hydrator: FormHydrator,
|
|
29
|
+
override readonly serializer: FormSerializer,
|
|
30
|
+
) {
|
|
31
|
+
super(hydrator, serializer);
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
handlers = [mainHandler, sectionAHandler]
|
|
33
|
+
const sectionAHandler = inject(SectionAHandler);
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
form = new FormGroup({});
|
|
36
|
+
handlers = [sectionAHandler]
|
|
37
|
+
|
|
38
|
+
this.initialize({ form, handlers });
|
|
36
39
|
}
|
|
37
40
|
}
|
|
38
41
|
```
|
|
39
42
|
|
|
40
|
-
---
|
|
41
|
-
|
|
42
43
|
### FormHandlerBase
|
|
43
44
|
|
|
44
45
|
Encapsulates reactive logic.
|
|
@@ -53,6 +54,14 @@ export class SectionAHandler extends FormHandlerBase<ControlNames> {
|
|
|
53
54
|
override getReactiveLogic(form?: FormGroup): Subscription {
|
|
54
55
|
this.registerControls(form, [...CONTROL_NAMES]);
|
|
55
56
|
|
|
57
|
+
const sub = new Subscription();
|
|
58
|
+
|
|
59
|
+
sub.add(this.reactiveMethod());
|
|
60
|
+
|
|
61
|
+
return sub;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private reactiveMethod() {
|
|
56
65
|
return this.valueChangesOf("fieldA").subscribe((value) => {
|
|
57
66
|
if (value) {
|
|
58
67
|
this.controls.dependentField.enable();
|
|
@@ -64,22 +73,21 @@ export class SectionAHandler extends FormHandlerBase<ControlNames> {
|
|
|
64
73
|
}
|
|
65
74
|
```
|
|
66
75
|
|
|
67
|
-
---
|
|
68
|
-
|
|
69
76
|
### FormMapperBase
|
|
70
77
|
|
|
71
|
-
Handles transformations between API and form.
|
|
78
|
+
Handles transformations between API and form. Optional: `FormHydrator` and `FormSerializer` will automatically map correlated values.
|
|
72
79
|
|
|
73
80
|
```ts
|
|
74
|
-
export class ExampleMapper extends FormMapperBase<ApiModel, RequestModel> {
|
|
75
|
-
|
|
81
|
+
export class ExampleMapper extends FormMapperBase<ApiModel, RequestModel, FormModel> {
|
|
82
|
+
toRequest(form: FormGroup): RequestModel {
|
|
83
|
+
const fieldA = form.value.fieldA ?? null;
|
|
76
84
|
return {
|
|
77
|
-
fieldA:
|
|
85
|
+
fieldA: fieldA?.replace(/_/g, " "),
|
|
78
86
|
fieldB: form.value.fieldB,
|
|
79
87
|
};
|
|
80
88
|
}
|
|
81
89
|
|
|
82
|
-
|
|
90
|
+
fromModel(model: ApiModel): FormModel {
|
|
83
91
|
return {
|
|
84
92
|
fieldA: model.fieldA,
|
|
85
93
|
fieldB: model.fieldB,
|
|
@@ -88,27 +96,85 @@ export class ExampleMapper extends FormMapperBase<ApiModel, RequestModel> {
|
|
|
88
96
|
}
|
|
89
97
|
```
|
|
90
98
|
|
|
91
|
-
|
|
99
|
+
### FormControlBase
|
|
100
|
+
|
|
101
|
+
Provides the ControlValueAccessor implementation as well as common component inputs such as label, placeholder, etc.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
@Component({
|
|
105
|
+
template: `
|
|
106
|
+
<input
|
|
107
|
+
[value]="displayValue()"
|
|
108
|
+
[disabled]="disabled()"
|
|
109
|
+
[required]="isRequired()"
|
|
110
|
+
(blur)="onTouched()"
|
|
111
|
+
(input)="onInput($event)" />
|
|
112
|
+
`,
|
|
113
|
+
})
|
|
114
|
+
export class CustomInput extends FormControlBase<string | null> {
|
|
115
|
+
displayValue = signal<string | null>(null);
|
|
116
|
+
|
|
117
|
+
override writeValue(value: number | null): void {
|
|
118
|
+
super.writeValue(value);
|
|
119
|
+
this.displayValue.set(value != null ? formatNumber(value) : null);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
onInput(event: Event) {
|
|
123
|
+
if (this._disabled()) return;
|
|
124
|
+
|
|
125
|
+
const rawValue: string | null = (event.target as HTMLInputElement).value ?? null;
|
|
126
|
+
const value: number = parseNumber(rawValue);
|
|
127
|
+
|
|
128
|
+
this.displayValue.set(value != null ? formatNumber(value) : null);
|
|
129
|
+
|
|
130
|
+
this.onChange(value);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Reusable Input Behaviors
|
|
136
|
+
|
|
137
|
+
Behaviors are just plain JavaScript objects:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
export class CurrencyBehavior {
|
|
141
|
+
blockNonDigitKey(event: KeyboardEvent) {
|
|
142
|
+
const input = event.target as HTMLInputElement;
|
|
143
|
+
const value = input.value;
|
|
144
|
+
|
|
145
|
+
if (event.ctrlKey || event.metaKey) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
92
148
|
|
|
93
|
-
|
|
149
|
+
// Custom logic...
|
|
94
150
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
| **Mapper** | Handles data transformation between API and Form states. |
|
|
151
|
+
event.preventDefault();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
100
155
|
|
|
101
|
-
|
|
156
|
+
## Available Input Components
|
|
102
157
|
|
|
103
|
-
|
|
158
|
+
All inputs share a consistent API and can be swapped between native and Material implementations without changing form logic.
|
|
104
159
|
|
|
105
|
-
|
|
160
|
+
| Input Type | Native Selector | Material Selector | Description |
|
|
161
|
+
|-----------------|--------------------------------|------------------------------------|----------------------------------------------------------------------------------|
|
|
162
|
+
| Text / Password | `nmf-text` | `nmf-mat-text` | Supports multiple input types including password with visibility toggle |
|
|
163
|
+
| Number | `nmf-number` | `nmf-mat-number` | Numeric input with type-safe value handling |
|
|
164
|
+
| Currency | `nmf-currency` | `nmf-mat-currency` | Formatted currency input with parsing and display formatting |
|
|
165
|
+
| Date | `nmf-datepicker` | `nmf-mat-datepicker` | Date selection with native or Angular Material datepicker UI |
|
|
166
|
+
| Time | `nmf-timepicker` | `nmf-mat-timepicker` | Time input with structured formatting |
|
|
167
|
+
| Select | `nmf-select` | `nmf-mat-select` | Dropdown/select with support for disabled options |
|
|
168
|
+
| Textarea | `nmf-textarea` | `nmf-mat-textarea` | Multi-line text input with configurable rows |
|
|
106
169
|
|
|
107
|
-
|
|
170
|
+
### Shared Features
|
|
108
171
|
|
|
109
|
-
-
|
|
110
|
-
-
|
|
111
|
-
-
|
|
172
|
+
- Implements `ControlValueAccessor`
|
|
173
|
+
- Fully compatible with Angular Reactive Forms
|
|
174
|
+
- Consistent API across all inputs
|
|
175
|
+
- Built-in validation state + error messaging
|
|
176
|
+
- Label, required indicator, and loading state support
|
|
177
|
+
- Behavior-driven input handling (formatting, parsing, restrictions)
|
|
112
178
|
|
|
113
179
|
## License
|
|
114
180
|
|