@ng-modular-forms/core 0.7.0 → 0.7.2
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 +28 -113
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -6,11 +6,7 @@ Core primitives, behaviors, and input components for orchestrating complex Angul
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install @ng-modular-forms/core
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
There is also an optional package which supports Angular Material UI components:
|
|
12
|
-
|
|
13
|
-
```bash
|
|
9
|
+
# Optional Material UI bindings:
|
|
14
10
|
npm install @ng-modular-forms/material
|
|
15
11
|
```
|
|
16
12
|
|
|
@@ -29,45 +25,27 @@ export class ExampleComponent extends FormOrchestrator {
|
|
|
29
25
|
override readonly serializer: FormSerializer,
|
|
30
26
|
) {
|
|
31
27
|
super(hydrator, serializer);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
handlers = [sectionAHandler]
|
|
37
|
-
|
|
38
|
-
this.initialize({ form, handlers });
|
|
28
|
+
this.initialize({
|
|
29
|
+
form: new FormGroup({}),
|
|
30
|
+
handlerRegistry: [inject(SectionAHandler)]
|
|
31
|
+
});
|
|
39
32
|
}
|
|
40
33
|
}
|
|
41
34
|
```
|
|
42
35
|
|
|
43
36
|
### FormHandlerBase
|
|
44
37
|
|
|
45
|
-
Encapsulates reactive logic.
|
|
38
|
+
Encapsulates reactive logic (e.g., if Field A changes, disable Field B). Keeps UI logic out of the component.
|
|
46
39
|
|
|
47
40
|
```ts
|
|
48
|
-
const CONTROL_NAMES = ["fieldA", "dependentField"] as const;
|
|
49
|
-
|
|
50
|
-
type ControlNames = (typeof CONTROL_NAMES)[number];
|
|
51
|
-
|
|
52
41
|
@Injectable()
|
|
53
|
-
export class SectionAHandler extends FormHandlerBase<
|
|
42
|
+
export class SectionAHandler extends FormHandlerBase<'fieldA' | 'fieldB'> {
|
|
54
43
|
override getReactiveLogic(form?: FormGroup): Subscription {
|
|
55
|
-
this.registerControls(form, [
|
|
56
|
-
|
|
57
|
-
const sub = new Subscription();
|
|
58
|
-
|
|
59
|
-
sub.add(this.reactiveMethod());
|
|
60
|
-
|
|
61
|
-
return sub;
|
|
62
|
-
}
|
|
44
|
+
this.registerControls(form, ["fieldA", "fieldB"]);
|
|
63
45
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
this.controls.dependentField.enable();
|
|
68
|
-
} else {
|
|
69
|
-
this.controls.dependentField.disable();
|
|
70
|
-
}
|
|
46
|
+
return this.valueChangesOf("fieldA").subscribe(val => {
|
|
47
|
+
const fieldB = getControl("fieldB", form);
|
|
48
|
+
val ? fieldB.enable() : fieldB.disable();
|
|
71
49
|
});
|
|
72
50
|
}
|
|
73
51
|
}
|
|
@@ -75,106 +53,43 @@ export class SectionAHandler extends FormHandlerBase<ControlNames> {
|
|
|
75
53
|
|
|
76
54
|
### FormMapperBase
|
|
77
55
|
|
|
78
|
-
Handles transformations between API and form.
|
|
56
|
+
Handles transformations between API and form. `FormHydrator` and `FormSerializer` will call these automatically.
|
|
79
57
|
|
|
80
58
|
```ts
|
|
81
59
|
export class ExampleMapper extends FormMapperBase<ApiModel, RequestModel, FormModel> {
|
|
82
60
|
toRequest(form: FormGroup): RequestModel {
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
fieldA: fieldA?.replace(/_/g, " "),
|
|
86
|
-
fieldB: form.value.fieldB,
|
|
87
|
-
};
|
|
61
|
+
return { fieldA: form.value.fieldA?.trim() };
|
|
88
62
|
}
|
|
89
63
|
|
|
90
64
|
fromModel(model: ApiModel): FormModel {
|
|
91
|
-
return {
|
|
92
|
-
fieldA: model.fieldA,
|
|
93
|
-
fieldB: model.fieldB,
|
|
94
|
-
};
|
|
65
|
+
return { fieldA: model.fieldA };
|
|
95
66
|
}
|
|
96
67
|
}
|
|
97
68
|
```
|
|
98
69
|
|
|
99
70
|
### FormControlBase
|
|
100
71
|
|
|
101
|
-
Provides
|
|
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
|
-
}
|
|
148
|
-
|
|
149
|
-
// Custom logic...
|
|
150
|
-
|
|
151
|
-
event.preventDefault();
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
```
|
|
72
|
+
Provides ControlValueAccessor boilerplate and common UI inputs (labels, hints, error states) for custom components.
|
|
155
73
|
|
|
156
74
|
## Available Input Components
|
|
157
75
|
|
|
158
|
-
All
|
|
76
|
+
All components share a consistent API and are interchangeable between Native and Material implementations without changing form logic.
|
|
159
77
|
|
|
160
|
-
| Input Type | Native Selector
|
|
161
|
-
|
|
162
|
-
| Text / Password | `nmf-text`
|
|
163
|
-
| Number | `nmf-number`
|
|
164
|
-
| Currency | `nmf-currency`
|
|
165
|
-
| Date | `nmf-datepicker`
|
|
166
|
-
| Time | `nmf-timepicker`
|
|
167
|
-
| Select | `nmf-select`
|
|
168
|
-
| Textarea | `nmf-textarea`
|
|
78
|
+
| Input Type | Native Selector | Material Selector |
|
|
79
|
+
|-----------------|----------------------|-----------------------|
|
|
80
|
+
| Text / Password | `nmf-text` | `nmf-mat-text` |
|
|
81
|
+
| Number | `nmf-number` | `nmf-mat-number` |
|
|
82
|
+
| Currency | `nmf-currency` | `nmf-mat-currency` |
|
|
83
|
+
| Date | `nmf-datepicker` | `nmf-mat-datepicker` |
|
|
84
|
+
| Time | `nmf-timepicker` | `nmf-mat-timepicker` |
|
|
85
|
+
| Select | `nmf-select` | `nmf-mat-select` |
|
|
86
|
+
| Textarea | `nmf-textarea` | `nmf-mat-textarea` |
|
|
169
87
|
|
|
170
88
|
### Shared Features
|
|
171
89
|
|
|
172
|
-
-
|
|
173
|
-
-
|
|
174
|
-
-
|
|
175
|
-
- Built-in validation state + error messaging
|
|
176
|
-
- Label, required indicator, and loading state support
|
|
177
|
-
- Behavior-driven input handling (formatting, parsing, restrictions)
|
|
90
|
+
- **CVA Compatible:** Works with formControlName.
|
|
91
|
+
- **Behavior-Driven:** Reusable logic for masking, parsing, and restrictions.
|
|
92
|
+
- **Validation:** Integrated error messaging and state handling.
|
|
178
93
|
|
|
179
94
|
## License
|
|
180
95
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ng-modular-forms/core",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Core primitives, behaviors, and input components for orchestrating complex Angular reactive forms.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"input-components",
|
|
16
16
|
"ng-modular-forms"
|
|
17
17
|
],
|
|
18
|
-
"homepage": "https://
|
|
18
|
+
"homepage": "https://ngmf.ronbodnar.com",
|
|
19
19
|
"repository": {
|
|
20
20
|
"type": "git",
|
|
21
21
|
"url": "https://github.com/ronbodnar/ng-modular-forms.git"
|