@relax.js/core 1.0.2 → 1.0.4
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 +194 -188
- package/dist/DependencyInjection.d.ts +42 -24
- package/dist/collections/Index.d.ts +2 -0
- package/dist/collections/index.js +1 -1
- package/dist/collections/index.js.map +4 -4
- package/dist/collections/index.mjs +1 -1
- package/dist/collections/index.mjs.map +4 -4
- package/dist/di/index.js +1 -1
- package/dist/di/index.js.map +3 -3
- package/dist/di/index.mjs +1 -1
- package/dist/di/index.mjs.map +3 -3
- package/dist/errors.d.ts +20 -0
- package/dist/forms/FormValidator.d.ts +1 -20
- package/dist/forms/ValidationRules.d.ts +2 -0
- package/dist/forms/index.js +1 -1
- package/dist/forms/index.js.map +4 -4
- package/dist/forms/index.mjs +1 -1
- package/dist/forms/index.mjs.map +4 -4
- package/dist/html/TableRenderer.d.ts +1 -0
- package/dist/html/index.js.map +2 -2
- package/dist/html/index.mjs.map +2 -2
- package/dist/html/template.d.ts +4 -0
- package/dist/http/http.d.ts +1 -0
- package/dist/http/index.js.map +2 -2
- package/dist/http/index.mjs.map +2 -2
- package/dist/index.d.ts +0 -2
- package/dist/index.js +3 -3
- package/dist/index.js.map +4 -4
- package/dist/index.mjs +3 -3
- package/dist/index.mjs.map +4 -4
- package/dist/routing/index.js +3 -3
- package/dist/routing/index.js.map +3 -3
- package/dist/routing/index.mjs +3 -3
- package/dist/routing/index.mjs.map +3 -3
- package/dist/routing/routeTargetRegistry.d.ts +1 -0
- package/dist/routing/types.d.ts +2 -1
- package/dist/templates/NodeTemplate.d.ts +2 -0
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +2 -2
- package/dist/utils/index.mjs +1 -1
- package/dist/utils/index.mjs.map +2 -2
- package/docs/Architecture.md +333 -333
- package/docs/DependencyInjection.md +277 -237
- package/docs/Errors.md +87 -87
- package/docs/GettingStarted.md +231 -231
- package/docs/Pipes.md +5 -5
- package/docs/Translations.md +167 -312
- package/docs/WhyRelaxjs.md +336 -336
- package/docs/api/.nojekyll +1 -0
- package/docs/api/assets/hierarchy.js +1 -0
- package/docs/api/assets/highlight.css +120 -0
- package/docs/api/assets/icons.js +18 -0
- package/docs/api/assets/icons.svg +1 -0
- package/docs/api/assets/main.js +60 -0
- package/docs/api/assets/navigation.js +1 -0
- package/docs/api/assets/search.js +1 -0
- package/docs/api/assets/style.css +1633 -0
- package/docs/api/classes/http.WebSocketClient.html +26 -0
- package/docs/api/classes/i18n.LocaleChangeEvent.html +66 -0
- package/docs/api/classes/index.Blueprint.html +3 -0
- package/docs/api/classes/index.BoundNode.html +3 -0
- package/docs/api/classes/index.DigitsValidation.html +10 -0
- package/docs/api/classes/index.FormValidator.html +32 -0
- package/docs/api/classes/index.HttpError.html +13 -0
- package/docs/api/classes/index.LinkedList.html +26 -0
- package/docs/api/classes/index.NavigateRouteEvent.html +76 -0
- package/docs/api/classes/index.Node.html +15 -0
- package/docs/api/classes/index.PageSelectedEvent.html +61 -0
- package/docs/api/classes/index.Pager.html +4 -0
- package/docs/api/classes/index.RangeValidation.html +15 -0
- package/docs/api/classes/index.RelaxError.html +17 -0
- package/docs/api/classes/index.RequiredValidation.html +10 -0
- package/docs/api/classes/index.RouteError.html +11 -0
- package/docs/api/classes/index.RouteGuardError.html +12 -0
- package/docs/api/classes/index.RouteLink.html +779 -0
- package/docs/api/classes/index.RouteTarget.html +788 -0
- package/docs/api/classes/index.SSEClient.html +13 -0
- package/docs/api/classes/index.SSEDataEvent.html +63 -0
- package/docs/api/classes/index.ServiceCollection.html +28 -0
- package/docs/api/classes/index.ServiceContainer.html +24 -0
- package/docs/api/classes/index.SortChangeEvent.html +61 -0
- package/docs/api/classes/index.TableRenderer.html +5 -0
- package/docs/api/classes/index.TableSorter.html +4 -0
- package/docs/api/enums/index.GuardResult.html +9 -0
- package/docs/api/functions/elements.formError.html +6 -0
- package/docs/api/functions/elements.selectOne.html +6 -0
- package/docs/api/functions/i18n.getCurrentLocale.html +3 -0
- package/docs/api/functions/i18n.loadNamespace.html +7 -0
- package/docs/api/functions/i18n.loadNamespaces.html +6 -0
- package/docs/api/functions/i18n.onMissingTranslation.html +7 -0
- package/docs/api/functions/i18n.setLocale.html +7 -0
- package/docs/api/functions/i18n.setMessageFormatter.html +7 -0
- package/docs/api/functions/i18n.t.html +9 -0
- package/docs/api/functions/index.BooleanConverter.html +6 -0
- package/docs/api/functions/index.ContainerService.html +13 -0
- package/docs/api/functions/index.DateConverter.html +11 -0
- package/docs/api/functions/index.Inject.html +16 -0
- package/docs/api/functions/index.NumberConverter.html +5 -0
- package/docs/api/functions/index.RegisterValidator.html +7 -0
- package/docs/api/functions/index.applyPipes.html +17 -0
- package/docs/api/functions/index.asyncHandler.html +11 -0
- package/docs/api/functions/index.capitalizePipe.html +4 -0
- package/docs/api/functions/index.clearPendingNavigations.html +1 -0
- package/docs/api/functions/index.compileTemplate.html +26 -0
- package/docs/api/functions/index.configure.html +5 -0
- package/docs/api/functions/index.createBluePrint.html +1 -0
- package/docs/api/functions/index.createConverterFromDataType.html +4 -0
- package/docs/api/functions/index.createConverterFromInputType.html +5 -0
- package/docs/api/functions/index.createPipeRegistry.html +12 -0
- package/docs/api/functions/index.currencyPipe.html +9 -0
- package/docs/api/functions/index.datePipe.html +9 -0
- package/docs/api/functions/index.daysAgoPipe.html +8 -0
- package/docs/api/functions/index.defaultPipe.html +5 -0
- package/docs/api/functions/index.defineRoutes.html +8 -0
- package/docs/api/functions/index.del.html +8 -0
- package/docs/api/functions/index.findRouteByName.html +5 -0
- package/docs/api/functions/index.findRouteByUrl.html +4 -0
- package/docs/api/functions/index.firstPipe.html +4 -0
- package/docs/api/functions/index.generateSequentialId.html +21 -0
- package/docs/api/functions/index.get.html +9 -0
- package/docs/api/functions/index.getDataConverter.html +11 -0
- package/docs/api/functions/index.getParentComponent.html +18 -0
- package/docs/api/functions/index.getValidator.html +4 -0
- package/docs/api/functions/index.html.html +19 -0
- package/docs/api/functions/index.joinPipe.html +5 -0
- package/docs/api/functions/index.keysPipe.html +4 -0
- package/docs/api/functions/index.lastPipe.html +4 -0
- package/docs/api/functions/index.lowercasePipe.html +4 -0
- package/docs/api/functions/index.mapFormToClass.html +17 -0
- package/docs/api/functions/index.matchRoute.html +5 -0
- package/docs/api/functions/index.navigate.html +8 -0
- package/docs/api/functions/index.onError.html +8 -0
- package/docs/api/functions/index.piecesPipe.html +8 -0
- package/docs/api/functions/index.post.html +9 -0
- package/docs/api/functions/index.printRoutes.html +2 -0
- package/docs/api/functions/index.put.html +9 -0
- package/docs/api/functions/index.readData.html +17 -0
- package/docs/api/functions/index.registerRouteTarget.html +9 -0
- package/docs/api/functions/index.reportError.html +10 -0
- package/docs/api/functions/index.request.html +8 -0
- package/docs/api/functions/index.resolveValue.html +18 -0
- package/docs/api/functions/index.setFetch.html +6 -0
- package/docs/api/functions/index.setFormData.html +17 -0
- package/docs/api/functions/index.shortenPipe.html +5 -0
- package/docs/api/functions/index.startRouting.html +6 -0
- package/docs/api/functions/index.ternaryPipe.html +6 -0
- package/docs/api/functions/index.trimPipe.html +4 -0
- package/docs/api/functions/index.unregisterRouteTarget.html +3 -0
- package/docs/api/functions/index.uppercasePipe.html +4 -0
- package/docs/api/hierarchy.html +1 -0
- package/docs/api/index.html +323 -0
- package/docs/api/interfaces/http.SimpleDataEvent.html +3 -0
- package/docs/api/interfaces/http.WebSocketAbstraction.html +9 -0
- package/docs/api/interfaces/http.WebSocketCodec.html +4 -0
- package/docs/api/interfaces/http.WebSocketOptions.html +20 -0
- package/docs/api/interfaces/index.CompiledTemplate.html +10 -0
- package/docs/api/interfaces/index.DataLoader.html +19 -0
- package/docs/api/interfaces/index.EngineConfig.html +11 -0
- package/docs/api/interfaces/index.ErrorContext.html +4 -0
- package/docs/api/interfaces/index.FormReaderOptions.html +8 -0
- package/docs/api/interfaces/index.HttpOptions.html +16 -0
- package/docs/api/interfaces/index.HttpResponse.html +17 -0
- package/docs/api/interfaces/index.LoadRoute.html +7 -0
- package/docs/api/interfaces/index.NavigateOptions.html +7 -0
- package/docs/api/interfaces/index.PipeRegistry.html +12 -0
- package/docs/api/interfaces/index.RegistrationOptions.html +22 -0
- package/docs/api/interfaces/index.RenderTemplate.html +7 -0
- package/docs/api/interfaces/index.RequestOptions.html +11 -0
- package/docs/api/interfaces/index.Routable.html +10 -0
- package/docs/api/interfaces/index.Route.html +13 -0
- package/docs/api/interfaces/index.RouteGuard.html +2 -0
- package/docs/api/interfaces/index.RouteValue.html +6 -0
- package/docs/api/interfaces/index.SSEOptions.html +24 -0
- package/docs/api/interfaces/index.ValidationContext.html +8 -0
- package/docs/api/interfaces/index.ValidatorOptions.html +14 -0
- package/docs/api/media/Architecture.md +333 -0
- package/docs/api/media/DependencyInjection.md +277 -0
- package/docs/api/media/GettingStarted.md +231 -0
- package/docs/api/media/HttpClient.md +459 -0
- package/docs/api/media/Pipes.md +211 -0
- package/docs/api/media/Routing.md +332 -0
- package/docs/api/media/WhyRelaxjs.md +336 -0
- package/docs/api/media/forms.md +99 -0
- package/docs/api/media/html.md +175 -0
- package/docs/api/media/i18n.md +354 -0
- package/docs/api/media/utilities.md +143 -0
- package/docs/api/media/validation.md +351 -0
- package/docs/api/modules/collections_Index.html +1 -0
- package/docs/api/modules/di.html +1 -0
- package/docs/api/modules/elements.html +1 -0
- package/docs/api/modules/forms.html +1 -0
- package/docs/api/modules/html.html +1 -0
- package/docs/api/modules/http.html +1 -0
- package/docs/api/modules/i18n.html +1 -0
- package/docs/api/modules/index.html +1 -0
- package/docs/api/modules/routing.html +1 -0
- package/docs/api/modules/utils.html +1 -0
- package/docs/api/modules.html +1 -0
- package/docs/api/types/http.WebSocketFactory.html +2 -0
- package/docs/api/types/i18n.MessageFormatter.html +3 -0
- package/docs/api/types/i18n.MissingTranslationHandler.html +1 -0
- package/docs/api/types/index.Constructor.html +7 -0
- package/docs/api/types/index.ConverterFunc.html +2 -0
- package/docs/api/types/index.DataType.html +2 -0
- package/docs/api/types/index.InputType.html +2 -0
- package/docs/api/types/index.PipeFunction.html +6 -0
- package/docs/api/types/index.RouteData.html +1 -0
- package/docs/api/types/index.RouteMatchResult.html +9 -0
- package/docs/api/types/index.RouteParamType.html +1 -0
- package/docs/api/types/index.RouteSegmentType.html +2 -0
- package/docs/api/types/index.SSEEventFactory.html +5 -0
- package/docs/api/types/index.ServiceScope.html +10 -0
- package/docs/api/types/index.SortColumn.html +3 -0
- package/docs/api/variables/i18n.formatICU.html +3 -0
- package/docs/api/variables/index.container.html +6 -0
- package/docs/api/variables/index.defaultPipes.html +6 -0
- package/docs/api/variables/index.internalRoutes.html +1 -0
- package/docs/api/variables/index.serviceCollection.html +6 -0
- package/docs/api.json +93171 -0
- package/docs/elements/dom.md +102 -102
- package/docs/forms/creating-form-components.md +924 -924
- package/docs/forms/form-api.md +94 -94
- package/docs/forms/forms.md +99 -99
- package/docs/forms/patterns.md +311 -311
- package/docs/forms/reading-writing.md +365 -365
- package/docs/forms/validation.md +351 -351
- package/docs/html/TableRenderer.md +291 -291
- package/docs/html/html.md +175 -175
- package/docs/html/index.md +54 -54
- package/docs/html/template.md +422 -422
- package/docs/http/HttpClient.md +459 -459
- package/docs/http/ServerSentEvents.md +184 -184
- package/docs/http/index.md +109 -109
- package/docs/i18n/i18n.md +49 -4
- package/docs/i18n/intl-standard.md +178 -178
- package/docs/routing/RouteLink.md +98 -98
- package/docs/routing/Routing.md +332 -332
- package/docs/routing/layouts.md +207 -207
- package/docs/utilities.md +143 -143
- package/package.json +4 -3
|
@@ -1,365 +1,365 @@
|
|
|
1
|
-
# Reading & Writing Form Data
|
|
2
|
-
|
|
3
|
-
Functions for reading and writing form data with automatic type conversion.
|
|
4
|
-
|
|
5
|
-
## mapFormToClass
|
|
6
|
-
|
|
7
|
-
Maps form field values to a class instance's properties with type conversion. Provides full type safety by populating an existing typed object.
|
|
8
|
-
|
|
9
|
-
```typescript
|
|
10
|
-
import { mapFormToClass } from '
|
|
11
|
-
|
|
12
|
-
class UserDTO {
|
|
13
|
-
name: string = '';
|
|
14
|
-
email: string = '';
|
|
15
|
-
age: number = 0;
|
|
16
|
-
newsletter: boolean = false;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const form = document.querySelector('form');
|
|
20
|
-
const user = mapFormToClass(form, new UserDTO());
|
|
21
|
-
console.log(user.name, user.age, user.newsletter);
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
### Options
|
|
25
|
-
|
|
26
|
-
```typescript
|
|
27
|
-
// Options object (inline, not a named interface)
|
|
28
|
-
{
|
|
29
|
-
throwOnMissingProperty?: boolean; // Throw if form field has no matching property
|
|
30
|
-
throwOnMissingField?: boolean; // Throw if class property has no matching field
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### Strict Validation Mode
|
|
35
|
-
|
|
36
|
-
Catch mismatches between form fields and your data model:
|
|
37
|
-
|
|
38
|
-
```typescript
|
|
39
|
-
const user = mapFormToClass(form, new UserDTO(), {
|
|
40
|
-
throwOnMissingProperty: true, // Catch typos in form field names
|
|
41
|
-
throwOnMissingField: true // Ensure all DTO fields are in form
|
|
42
|
-
});
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### Type Conversion
|
|
46
|
-
|
|
47
|
-
Automatic conversion based on input types:
|
|
48
|
-
|
|
49
|
-
| Input Type | Converted To |
|
|
50
|
-
|------------|--------------|
|
|
51
|
-
| `checkbox` | `boolean` |
|
|
52
|
-
| `number` | `number` |
|
|
53
|
-
| `date` | `Date` |
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
class ProductDTO {
|
|
57
|
-
name: string = '';
|
|
58
|
-
price: number = 0;
|
|
59
|
-
inStock: boolean = false;
|
|
60
|
-
releaseDate: Date | null = null;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const product = mapFormToClass(form, new ProductDTO());
|
|
64
|
-
// product.price is number, product.inStock is boolean
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## readData
|
|
68
|
-
|
|
69
|
-
Reads all form data into a plain object with automatic type conversion. Use when you don't need strict typing.
|
|
70
|
-
|
|
71
|
-
```typescript
|
|
72
|
-
import { readData } from '
|
|
73
|
-
|
|
74
|
-
const form = document.querySelector('form');
|
|
75
|
-
const data = readData(form);
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### Type Conversion
|
|
79
|
-
|
|
80
|
-
Type conversion is determined by:
|
|
81
|
-
1. `data-type` attribute if present (`number`, `boolean`, `string`, `Date`)
|
|
82
|
-
2. Input type (`checkbox`, `number`, `date`, etc.)
|
|
83
|
-
3. Falls back to string
|
|
84
|
-
|
|
85
|
-
```html
|
|
86
|
-
<form>
|
|
87
|
-
<input name="username" value="john" />
|
|
88
|
-
<input name="age" type="number" value="25" />
|
|
89
|
-
<input name="active" type="checkbox" checked />
|
|
90
|
-
<input name="salary" data-type="number" value="50000" />
|
|
91
|
-
<select name="colors" multiple>
|
|
92
|
-
<option value="red" selected>Red</option>
|
|
93
|
-
<option value="blue" selected>Blue</option>
|
|
94
|
-
</select>
|
|
95
|
-
</form>
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
const data = readData(form);
|
|
100
|
-
// Returns: {
|
|
101
|
-
// username: 'john',
|
|
102
|
-
// age: 25,
|
|
103
|
-
// active: true,
|
|
104
|
-
// salary: 50000,
|
|
105
|
-
// colors: ['red', 'blue']
|
|
106
|
-
// }
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Checkbox Handling
|
|
110
|
-
|
|
111
|
-
Unchecked checkboxes are included as `false` in the result. Checked checkboxes with no explicit `value` attribute (which submit as `'on'` in FormData) are correctly converted to `true`.
|
|
112
|
-
|
|
113
|
-
```html
|
|
114
|
-
<input name="active" type="checkbox" checked />
|
|
115
|
-
<input name="terms" type="checkbox" />
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
```typescript
|
|
119
|
-
const data = readData(form);
|
|
120
|
-
// Returns: { active: true, terms: false }
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### Custom Form Components
|
|
124
|
-
|
|
125
|
-
Works with form-associated custom elements:
|
|
126
|
-
|
|
127
|
-
```html
|
|
128
|
-
<form>
|
|
129
|
-
<r-input name="email" value="test@example.com"></r-input>
|
|
130
|
-
<r-checkbox name="terms" checked></r-checkbox>
|
|
131
|
-
</form>
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
const data = readData(form);
|
|
136
|
-
// Returns: { email: 'test@example.com', terms: true }
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
## setFormData
|
|
140
|
-
|
|
141
|
-
Populates form fields from a data object using the `name` attribute.
|
|
142
|
-
|
|
143
|
-
```typescript
|
|
144
|
-
import { setFormData } from '
|
|
145
|
-
|
|
146
|
-
const form = document.querySelector('form');
|
|
147
|
-
const data = { name: 'John', email: 'john@example.com' };
|
|
148
|
-
setFormData(form, data);
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### Nested Objects (Dot Notation)
|
|
152
|
-
|
|
153
|
-
```html
|
|
154
|
-
<form>
|
|
155
|
-
<input name="user.name" />
|
|
156
|
-
<input name="user.contact.email" />
|
|
157
|
-
</form>
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
```typescript
|
|
161
|
-
const data = {
|
|
162
|
-
user: {
|
|
163
|
-
name: 'John',
|
|
164
|
-
contact: {
|
|
165
|
-
email: 'john@example.com'
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
setFormData(form, data);
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
### Date and DateTime
|
|
173
|
-
|
|
174
|
-
`setFormData` formats `Date` objects to the correct string format for each input type:
|
|
175
|
-
|
|
176
|
-
```html
|
|
177
|
-
<input type="date" name="birthday" />
|
|
178
|
-
<input type="datetime-local" name="meeting" />
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
```typescript
|
|
182
|
-
setFormData(form, {
|
|
183
|
-
birthday: new Date('2000-01-15'), // Sets "2000-01-15"
|
|
184
|
-
meeting: new Date('2024-06-15T14:30') // Sets "2024-06-15T14:30"
|
|
185
|
-
});
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
### Select Multiple
|
|
189
|
-
|
|
190
|
-
Array values are set directly on `<select multiple>` elements:
|
|
191
|
-
|
|
192
|
-
```html
|
|
193
|
-
<select name="colors" multiple>
|
|
194
|
-
<option value="red">Red</option>
|
|
195
|
-
<option value="green">Green</option>
|
|
196
|
-
<option value="blue">Blue</option>
|
|
197
|
-
</select>
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
```typescript
|
|
201
|
-
setFormData(form, { colors: ['red', 'blue'] });
|
|
202
|
-
// Selects "red" and "blue", deselects "green"
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
### Simple Arrays ([] Notation)
|
|
206
|
-
|
|
207
|
-
For checkboxes, multi-select, and text inputs:
|
|
208
|
-
|
|
209
|
-
```html
|
|
210
|
-
<form>
|
|
211
|
-
<input name="hobbies[]" type="checkbox" value="Reading" />
|
|
212
|
-
<input name="hobbies[]" type="checkbox" value="Cycling" />
|
|
213
|
-
<input name="hobbies[]" type="checkbox" value="Cooking" />
|
|
214
|
-
</form>
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
```typescript
|
|
218
|
-
const data = {
|
|
219
|
-
hobbies: ['Reading', 'Cooking']
|
|
220
|
-
};
|
|
221
|
-
setFormData(form, data);
|
|
222
|
-
// Checks "Reading" and "Cooking" checkboxes
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### Array of Objects (Indexed Notation)
|
|
226
|
-
|
|
227
|
-
```html
|
|
228
|
-
<form>
|
|
229
|
-
<input name="users[0].name" />
|
|
230
|
-
<input name="users[0].email" />
|
|
231
|
-
<input name="users[1].name" />
|
|
232
|
-
<input name="users[1].email" />
|
|
233
|
-
</form>
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
```typescript
|
|
237
|
-
const data = {
|
|
238
|
-
users: [
|
|
239
|
-
{ name: 'John', email: 'john@example.com' },
|
|
240
|
-
{ name: 'Jane', email: 'jane@example.com' }
|
|
241
|
-
]
|
|
242
|
-
};
|
|
243
|
-
setFormData(form, data);
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
### Complex Data Structures
|
|
247
|
-
|
|
248
|
-
```html
|
|
249
|
-
<form id="product-form">
|
|
250
|
-
<input name="product.name" placeholder="Product Name">
|
|
251
|
-
<input name="product.price" type="number" placeholder="Price">
|
|
252
|
-
|
|
253
|
-
<input name="categories[]" type="checkbox" value="electronics">
|
|
254
|
-
<input name="categories[]" type="checkbox" value="gadgets">
|
|
255
|
-
|
|
256
|
-
<input name="variants[0].size" placeholder="Size">
|
|
257
|
-
<input name="variants[0].color" placeholder="Color">
|
|
258
|
-
<input name="variants[1].size" placeholder="Size">
|
|
259
|
-
<input name="variants[1].color" placeholder="Color">
|
|
260
|
-
|
|
261
|
-
<input name="supplier.company" placeholder="Company">
|
|
262
|
-
<input name="supplier.contact.email" placeholder="Email">
|
|
263
|
-
</form>
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
```typescript
|
|
267
|
-
const productData = {
|
|
268
|
-
product: {
|
|
269
|
-
name: 'Wireless Headphones',
|
|
270
|
-
price: 99.99
|
|
271
|
-
},
|
|
272
|
-
categories: ['electronics', 'gadgets'],
|
|
273
|
-
variants: [
|
|
274
|
-
{ size: 'M', color: 'black' },
|
|
275
|
-
{ size: 'L', color: 'white' }
|
|
276
|
-
],
|
|
277
|
-
supplier: {
|
|
278
|
-
company: 'TechCorp',
|
|
279
|
-
contact: {
|
|
280
|
-
email: 'orders@techcorp.com'
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
setFormData(form, productData);
|
|
286
|
-
|
|
287
|
-
// Later, extract the data
|
|
288
|
-
const extractedData = readData(form);
|
|
289
|
-
// Result matches the original structure
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
## Type Converters
|
|
293
|
-
|
|
294
|
-
### Built-in Converters
|
|
295
|
-
|
|
296
|
-
| Converter | Input | Output |
|
|
297
|
-
|-----------|-------|--------|
|
|
298
|
-
| `BooleanConverter` | `'true'`, `'on'`, `'1'`, any positive number string -> `true`; `'false'`, `'off'`, `'0'` -> `false` | `boolean` |
|
|
299
|
-
| `NumberConverter` | Numeric string | `number` |
|
|
300
|
-
| `DateConverter` | ISO or locale-formatted date string | `Date` |
|
|
301
|
-
|
|
302
|
-
#### Locale-Aware Date Parsing
|
|
303
|
-
|
|
304
|
-
`DateConverter` detects the current locale and parses dates in the local format:
|
|
305
|
-
|
|
306
|
-
| Locale | Format | Example |
|
|
307
|
-
|--------|--------|---------|
|
|
308
|
-
| `en` (US) | `MM/DD/YYYY` | `01/15/2024` |
|
|
309
|
-
| `sv` (Swedish) | `YYYY-MM-DD` | `2024-01-15` |
|
|
310
|
-
| `de` (German) | `DD.MM.YYYY` | `15.01.2024` |
|
|
311
|
-
|
|
312
|
-
ISO format (`2024-01-15`) is always accepted regardless of locale.
|
|
313
|
-
|
|
314
|
-
### Custom Type via `data-type` Attribute
|
|
315
|
-
|
|
316
|
-
Use the `data-type` attribute to override conversion for any field:
|
|
317
|
-
|
|
318
|
-
```html
|
|
319
|
-
<input name="salary" data-type="number" value="50000" />
|
|
320
|
-
<input name="active" data-type="boolean" value="true" />
|
|
321
|
-
<input name="birthday" data-type="Date" value="2000-01-15" />
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
```typescript
|
|
325
|
-
const data = readData(form);
|
|
326
|
-
console.log(data.salary); // number: 50000
|
|
327
|
-
console.log(data.active); // boolean: true
|
|
328
|
-
console.log(data.birthday); // Date object
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
Supported `data-type` values: `number`, `boolean`, `string`, `Date`.
|
|
332
|
-
|
|
333
|
-
### Date and Time Handling
|
|
334
|
-
|
|
335
|
-
```html
|
|
336
|
-
<input name="startDate" type="date">
|
|
337
|
-
<input name="startTime" type="time">
|
|
338
|
-
<input name="duration" type="time" step="900"> <!-- 15 min steps -->
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
```typescript
|
|
342
|
-
// Custom handling for combining date and time
|
|
343
|
-
document.querySelector('[name="startTime"]').getData = function() {
|
|
344
|
-
const dateInput = form.querySelector('[name="startDate"]') as HTMLInputElement;
|
|
345
|
-
const timeInput = this as HTMLInputElement;
|
|
346
|
-
|
|
347
|
-
if (dateInput.value && timeInput.value) {
|
|
348
|
-
return new Date(`${dateInput.value}T${timeInput.value}`);
|
|
349
|
-
}
|
|
350
|
-
return null;
|
|
351
|
-
};
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
### Input Type Conversion Table
|
|
355
|
-
|
|
356
|
-
| Input Type | Converted To |
|
|
357
|
-
|------------|--------------|
|
|
358
|
-
| `checkbox` | `boolean` |
|
|
359
|
-
| `number` | `number` |
|
|
360
|
-
| `date` | `Date` |
|
|
361
|
-
| `datetime-local` | `Date` |
|
|
362
|
-
| `month` | `Date` (first of month) |
|
|
363
|
-
| `week` | `{ year, week }` |
|
|
364
|
-
| `time` | `{ hours, minutes, seconds }` |
|
|
365
|
-
| Default | `string` |
|
|
1
|
+
# Reading & Writing Form Data
|
|
2
|
+
|
|
3
|
+
Functions for reading and writing form data with automatic type conversion.
|
|
4
|
+
|
|
5
|
+
## mapFormToClass
|
|
6
|
+
|
|
7
|
+
Maps form field values to a class instance's properties with type conversion. Provides full type safety by populating an existing typed object.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { mapFormToClass } from '@relax.js/core/forms';
|
|
11
|
+
|
|
12
|
+
class UserDTO {
|
|
13
|
+
name: string = '';
|
|
14
|
+
email: string = '';
|
|
15
|
+
age: number = 0;
|
|
16
|
+
newsletter: boolean = false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const form = document.querySelector('form');
|
|
20
|
+
const user = mapFormToClass(form, new UserDTO());
|
|
21
|
+
console.log(user.name, user.age, user.newsletter);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Options
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
// Options object (inline, not a named interface)
|
|
28
|
+
{
|
|
29
|
+
throwOnMissingProperty?: boolean; // Throw if form field has no matching property
|
|
30
|
+
throwOnMissingField?: boolean; // Throw if class property has no matching field
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Strict Validation Mode
|
|
35
|
+
|
|
36
|
+
Catch mismatches between form fields and your data model:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
const user = mapFormToClass(form, new UserDTO(), {
|
|
40
|
+
throwOnMissingProperty: true, // Catch typos in form field names
|
|
41
|
+
throwOnMissingField: true // Ensure all DTO fields are in form
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Type Conversion
|
|
46
|
+
|
|
47
|
+
Automatic conversion based on input types:
|
|
48
|
+
|
|
49
|
+
| Input Type | Converted To |
|
|
50
|
+
|------------|--------------|
|
|
51
|
+
| `checkbox` | `boolean` |
|
|
52
|
+
| `number` | `number` |
|
|
53
|
+
| `date` | `Date` |
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
class ProductDTO {
|
|
57
|
+
name: string = '';
|
|
58
|
+
price: number = 0;
|
|
59
|
+
inStock: boolean = false;
|
|
60
|
+
releaseDate: Date | null = null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const product = mapFormToClass(form, new ProductDTO());
|
|
64
|
+
// product.price is number, product.inStock is boolean
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## readData
|
|
68
|
+
|
|
69
|
+
Reads all form data into a plain object with automatic type conversion. Use when you don't need strict typing.
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { readData } from '@relax.js/core/forms';
|
|
73
|
+
|
|
74
|
+
const form = document.querySelector('form');
|
|
75
|
+
const data = readData(form);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Type Conversion
|
|
79
|
+
|
|
80
|
+
Type conversion is determined by:
|
|
81
|
+
1. `data-type` attribute if present (`number`, `boolean`, `string`, `Date`)
|
|
82
|
+
2. Input type (`checkbox`, `number`, `date`, etc.)
|
|
83
|
+
3. Falls back to string
|
|
84
|
+
|
|
85
|
+
```html
|
|
86
|
+
<form>
|
|
87
|
+
<input name="username" value="john" />
|
|
88
|
+
<input name="age" type="number" value="25" />
|
|
89
|
+
<input name="active" type="checkbox" checked />
|
|
90
|
+
<input name="salary" data-type="number" value="50000" />
|
|
91
|
+
<select name="colors" multiple>
|
|
92
|
+
<option value="red" selected>Red</option>
|
|
93
|
+
<option value="blue" selected>Blue</option>
|
|
94
|
+
</select>
|
|
95
|
+
</form>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
const data = readData(form);
|
|
100
|
+
// Returns: {
|
|
101
|
+
// username: 'john',
|
|
102
|
+
// age: 25,
|
|
103
|
+
// active: true,
|
|
104
|
+
// salary: 50000,
|
|
105
|
+
// colors: ['red', 'blue']
|
|
106
|
+
// }
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Checkbox Handling
|
|
110
|
+
|
|
111
|
+
Unchecked checkboxes are included as `false` in the result. Checked checkboxes with no explicit `value` attribute (which submit as `'on'` in FormData) are correctly converted to `true`.
|
|
112
|
+
|
|
113
|
+
```html
|
|
114
|
+
<input name="active" type="checkbox" checked />
|
|
115
|
+
<input name="terms" type="checkbox" />
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
const data = readData(form);
|
|
120
|
+
// Returns: { active: true, terms: false }
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Custom Form Components
|
|
124
|
+
|
|
125
|
+
Works with form-associated custom elements:
|
|
126
|
+
|
|
127
|
+
```html
|
|
128
|
+
<form>
|
|
129
|
+
<r-input name="email" value="test@example.com"></r-input>
|
|
130
|
+
<r-checkbox name="terms" checked></r-checkbox>
|
|
131
|
+
</form>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
const data = readData(form);
|
|
136
|
+
// Returns: { email: 'test@example.com', terms: true }
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## setFormData
|
|
140
|
+
|
|
141
|
+
Populates form fields from a data object using the `name` attribute.
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
import { setFormData } from '@relax.js/core/forms';
|
|
145
|
+
|
|
146
|
+
const form = document.querySelector('form');
|
|
147
|
+
const data = { name: 'John', email: 'john@example.com' };
|
|
148
|
+
setFormData(form, data);
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Nested Objects (Dot Notation)
|
|
152
|
+
|
|
153
|
+
```html
|
|
154
|
+
<form>
|
|
155
|
+
<input name="user.name" />
|
|
156
|
+
<input name="user.contact.email" />
|
|
157
|
+
</form>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
const data = {
|
|
162
|
+
user: {
|
|
163
|
+
name: 'John',
|
|
164
|
+
contact: {
|
|
165
|
+
email: 'john@example.com'
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
setFormData(form, data);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Date and DateTime
|
|
173
|
+
|
|
174
|
+
`setFormData` formats `Date` objects to the correct string format for each input type:
|
|
175
|
+
|
|
176
|
+
```html
|
|
177
|
+
<input type="date" name="birthday" />
|
|
178
|
+
<input type="datetime-local" name="meeting" />
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
setFormData(form, {
|
|
183
|
+
birthday: new Date('2000-01-15'), // Sets "2000-01-15"
|
|
184
|
+
meeting: new Date('2024-06-15T14:30') // Sets "2024-06-15T14:30"
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Select Multiple
|
|
189
|
+
|
|
190
|
+
Array values are set directly on `<select multiple>` elements:
|
|
191
|
+
|
|
192
|
+
```html
|
|
193
|
+
<select name="colors" multiple>
|
|
194
|
+
<option value="red">Red</option>
|
|
195
|
+
<option value="green">Green</option>
|
|
196
|
+
<option value="blue">Blue</option>
|
|
197
|
+
</select>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
setFormData(form, { colors: ['red', 'blue'] });
|
|
202
|
+
// Selects "red" and "blue", deselects "green"
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Simple Arrays ([] Notation)
|
|
206
|
+
|
|
207
|
+
For checkboxes, multi-select, and text inputs:
|
|
208
|
+
|
|
209
|
+
```html
|
|
210
|
+
<form>
|
|
211
|
+
<input name="hobbies[]" type="checkbox" value="Reading" />
|
|
212
|
+
<input name="hobbies[]" type="checkbox" value="Cycling" />
|
|
213
|
+
<input name="hobbies[]" type="checkbox" value="Cooking" />
|
|
214
|
+
</form>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
const data = {
|
|
219
|
+
hobbies: ['Reading', 'Cooking']
|
|
220
|
+
};
|
|
221
|
+
setFormData(form, data);
|
|
222
|
+
// Checks "Reading" and "Cooking" checkboxes
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Array of Objects (Indexed Notation)
|
|
226
|
+
|
|
227
|
+
```html
|
|
228
|
+
<form>
|
|
229
|
+
<input name="users[0].name" />
|
|
230
|
+
<input name="users[0].email" />
|
|
231
|
+
<input name="users[1].name" />
|
|
232
|
+
<input name="users[1].email" />
|
|
233
|
+
</form>
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
const data = {
|
|
238
|
+
users: [
|
|
239
|
+
{ name: 'John', email: 'john@example.com' },
|
|
240
|
+
{ name: 'Jane', email: 'jane@example.com' }
|
|
241
|
+
]
|
|
242
|
+
};
|
|
243
|
+
setFormData(form, data);
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Complex Data Structures
|
|
247
|
+
|
|
248
|
+
```html
|
|
249
|
+
<form id="product-form">
|
|
250
|
+
<input name="product.name" placeholder="Product Name">
|
|
251
|
+
<input name="product.price" type="number" placeholder="Price">
|
|
252
|
+
|
|
253
|
+
<input name="categories[]" type="checkbox" value="electronics">
|
|
254
|
+
<input name="categories[]" type="checkbox" value="gadgets">
|
|
255
|
+
|
|
256
|
+
<input name="variants[0].size" placeholder="Size">
|
|
257
|
+
<input name="variants[0].color" placeholder="Color">
|
|
258
|
+
<input name="variants[1].size" placeholder="Size">
|
|
259
|
+
<input name="variants[1].color" placeholder="Color">
|
|
260
|
+
|
|
261
|
+
<input name="supplier.company" placeholder="Company">
|
|
262
|
+
<input name="supplier.contact.email" placeholder="Email">
|
|
263
|
+
</form>
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
const productData = {
|
|
268
|
+
product: {
|
|
269
|
+
name: 'Wireless Headphones',
|
|
270
|
+
price: 99.99
|
|
271
|
+
},
|
|
272
|
+
categories: ['electronics', 'gadgets'],
|
|
273
|
+
variants: [
|
|
274
|
+
{ size: 'M', color: 'black' },
|
|
275
|
+
{ size: 'L', color: 'white' }
|
|
276
|
+
],
|
|
277
|
+
supplier: {
|
|
278
|
+
company: 'TechCorp',
|
|
279
|
+
contact: {
|
|
280
|
+
email: 'orders@techcorp.com'
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
setFormData(form, productData);
|
|
286
|
+
|
|
287
|
+
// Later, extract the data
|
|
288
|
+
const extractedData = readData(form);
|
|
289
|
+
// Result matches the original structure
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Type Converters
|
|
293
|
+
|
|
294
|
+
### Built-in Converters
|
|
295
|
+
|
|
296
|
+
| Converter | Input | Output |
|
|
297
|
+
|-----------|-------|--------|
|
|
298
|
+
| `BooleanConverter` | `'true'`, `'on'`, `'1'`, any positive number string -> `true`; `'false'`, `'off'`, `'0'` -> `false` | `boolean` |
|
|
299
|
+
| `NumberConverter` | Numeric string | `number` |
|
|
300
|
+
| `DateConverter` | ISO or locale-formatted date string | `Date` |
|
|
301
|
+
|
|
302
|
+
#### Locale-Aware Date Parsing
|
|
303
|
+
|
|
304
|
+
`DateConverter` detects the current locale and parses dates in the local format:
|
|
305
|
+
|
|
306
|
+
| Locale | Format | Example |
|
|
307
|
+
|--------|--------|---------|
|
|
308
|
+
| `en` (US) | `MM/DD/YYYY` | `01/15/2024` |
|
|
309
|
+
| `sv` (Swedish) | `YYYY-MM-DD` | `2024-01-15` |
|
|
310
|
+
| `de` (German) | `DD.MM.YYYY` | `15.01.2024` |
|
|
311
|
+
|
|
312
|
+
ISO format (`2024-01-15`) is always accepted regardless of locale.
|
|
313
|
+
|
|
314
|
+
### Custom Type via `data-type` Attribute
|
|
315
|
+
|
|
316
|
+
Use the `data-type` attribute to override conversion for any field:
|
|
317
|
+
|
|
318
|
+
```html
|
|
319
|
+
<input name="salary" data-type="number" value="50000" />
|
|
320
|
+
<input name="active" data-type="boolean" value="true" />
|
|
321
|
+
<input name="birthday" data-type="Date" value="2000-01-15" />
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
const data = readData(form);
|
|
326
|
+
console.log(data.salary); // number: 50000
|
|
327
|
+
console.log(data.active); // boolean: true
|
|
328
|
+
console.log(data.birthday); // Date object
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Supported `data-type` values: `number`, `boolean`, `string`, `Date`.
|
|
332
|
+
|
|
333
|
+
### Date and Time Handling
|
|
334
|
+
|
|
335
|
+
```html
|
|
336
|
+
<input name="startDate" type="date">
|
|
337
|
+
<input name="startTime" type="time">
|
|
338
|
+
<input name="duration" type="time" step="900"> <!-- 15 min steps -->
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
// Custom handling for combining date and time
|
|
343
|
+
document.querySelector('[name="startTime"]').getData = function() {
|
|
344
|
+
const dateInput = form.querySelector('[name="startDate"]') as HTMLInputElement;
|
|
345
|
+
const timeInput = this as HTMLInputElement;
|
|
346
|
+
|
|
347
|
+
if (dateInput.value && timeInput.value) {
|
|
348
|
+
return new Date(`${dateInput.value}T${timeInput.value}`);
|
|
349
|
+
}
|
|
350
|
+
return null;
|
|
351
|
+
};
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Input Type Conversion Table
|
|
355
|
+
|
|
356
|
+
| Input Type | Converted To |
|
|
357
|
+
|------------|--------------|
|
|
358
|
+
| `checkbox` | `boolean` |
|
|
359
|
+
| `number` | `number` |
|
|
360
|
+
| `date` | `Date` |
|
|
361
|
+
| `datetime-local` | `Date` |
|
|
362
|
+
| `month` | `Date` (first of month) |
|
|
363
|
+
| `week` | `{ year, week }` |
|
|
364
|
+
| `time` | `{ hours, minutes, seconds }` |
|
|
365
|
+
| Default | `string` |
|