@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.
Files changed (240) hide show
  1. package/README.md +194 -188
  2. package/dist/DependencyInjection.d.ts +42 -24
  3. package/dist/collections/Index.d.ts +2 -0
  4. package/dist/collections/index.js +1 -1
  5. package/dist/collections/index.js.map +4 -4
  6. package/dist/collections/index.mjs +1 -1
  7. package/dist/collections/index.mjs.map +4 -4
  8. package/dist/di/index.js +1 -1
  9. package/dist/di/index.js.map +3 -3
  10. package/dist/di/index.mjs +1 -1
  11. package/dist/di/index.mjs.map +3 -3
  12. package/dist/errors.d.ts +20 -0
  13. package/dist/forms/FormValidator.d.ts +1 -20
  14. package/dist/forms/ValidationRules.d.ts +2 -0
  15. package/dist/forms/index.js +1 -1
  16. package/dist/forms/index.js.map +4 -4
  17. package/dist/forms/index.mjs +1 -1
  18. package/dist/forms/index.mjs.map +4 -4
  19. package/dist/html/TableRenderer.d.ts +1 -0
  20. package/dist/html/index.js.map +2 -2
  21. package/dist/html/index.mjs.map +2 -2
  22. package/dist/html/template.d.ts +4 -0
  23. package/dist/http/http.d.ts +1 -0
  24. package/dist/http/index.js.map +2 -2
  25. package/dist/http/index.mjs.map +2 -2
  26. package/dist/index.d.ts +0 -2
  27. package/dist/index.js +3 -3
  28. package/dist/index.js.map +4 -4
  29. package/dist/index.mjs +3 -3
  30. package/dist/index.mjs.map +4 -4
  31. package/dist/routing/index.js +3 -3
  32. package/dist/routing/index.js.map +3 -3
  33. package/dist/routing/index.mjs +3 -3
  34. package/dist/routing/index.mjs.map +3 -3
  35. package/dist/routing/routeTargetRegistry.d.ts +1 -0
  36. package/dist/routing/types.d.ts +2 -1
  37. package/dist/templates/NodeTemplate.d.ts +2 -0
  38. package/dist/utils/index.js +1 -1
  39. package/dist/utils/index.js.map +2 -2
  40. package/dist/utils/index.mjs +1 -1
  41. package/dist/utils/index.mjs.map +2 -2
  42. package/docs/Architecture.md +333 -333
  43. package/docs/DependencyInjection.md +277 -237
  44. package/docs/Errors.md +87 -87
  45. package/docs/GettingStarted.md +231 -231
  46. package/docs/Pipes.md +5 -5
  47. package/docs/Translations.md +167 -312
  48. package/docs/WhyRelaxjs.md +336 -336
  49. package/docs/api/.nojekyll +1 -0
  50. package/docs/api/assets/hierarchy.js +1 -0
  51. package/docs/api/assets/highlight.css +120 -0
  52. package/docs/api/assets/icons.js +18 -0
  53. package/docs/api/assets/icons.svg +1 -0
  54. package/docs/api/assets/main.js +60 -0
  55. package/docs/api/assets/navigation.js +1 -0
  56. package/docs/api/assets/search.js +1 -0
  57. package/docs/api/assets/style.css +1633 -0
  58. package/docs/api/classes/http.WebSocketClient.html +26 -0
  59. package/docs/api/classes/i18n.LocaleChangeEvent.html +66 -0
  60. package/docs/api/classes/index.Blueprint.html +3 -0
  61. package/docs/api/classes/index.BoundNode.html +3 -0
  62. package/docs/api/classes/index.DigitsValidation.html +10 -0
  63. package/docs/api/classes/index.FormValidator.html +32 -0
  64. package/docs/api/classes/index.HttpError.html +13 -0
  65. package/docs/api/classes/index.LinkedList.html +26 -0
  66. package/docs/api/classes/index.NavigateRouteEvent.html +76 -0
  67. package/docs/api/classes/index.Node.html +15 -0
  68. package/docs/api/classes/index.PageSelectedEvent.html +61 -0
  69. package/docs/api/classes/index.Pager.html +4 -0
  70. package/docs/api/classes/index.RangeValidation.html +15 -0
  71. package/docs/api/classes/index.RelaxError.html +17 -0
  72. package/docs/api/classes/index.RequiredValidation.html +10 -0
  73. package/docs/api/classes/index.RouteError.html +11 -0
  74. package/docs/api/classes/index.RouteGuardError.html +12 -0
  75. package/docs/api/classes/index.RouteLink.html +779 -0
  76. package/docs/api/classes/index.RouteTarget.html +788 -0
  77. package/docs/api/classes/index.SSEClient.html +13 -0
  78. package/docs/api/classes/index.SSEDataEvent.html +63 -0
  79. package/docs/api/classes/index.ServiceCollection.html +28 -0
  80. package/docs/api/classes/index.ServiceContainer.html +24 -0
  81. package/docs/api/classes/index.SortChangeEvent.html +61 -0
  82. package/docs/api/classes/index.TableRenderer.html +5 -0
  83. package/docs/api/classes/index.TableSorter.html +4 -0
  84. package/docs/api/enums/index.GuardResult.html +9 -0
  85. package/docs/api/functions/elements.formError.html +6 -0
  86. package/docs/api/functions/elements.selectOne.html +6 -0
  87. package/docs/api/functions/i18n.getCurrentLocale.html +3 -0
  88. package/docs/api/functions/i18n.loadNamespace.html +7 -0
  89. package/docs/api/functions/i18n.loadNamespaces.html +6 -0
  90. package/docs/api/functions/i18n.onMissingTranslation.html +7 -0
  91. package/docs/api/functions/i18n.setLocale.html +7 -0
  92. package/docs/api/functions/i18n.setMessageFormatter.html +7 -0
  93. package/docs/api/functions/i18n.t.html +9 -0
  94. package/docs/api/functions/index.BooleanConverter.html +6 -0
  95. package/docs/api/functions/index.ContainerService.html +13 -0
  96. package/docs/api/functions/index.DateConverter.html +11 -0
  97. package/docs/api/functions/index.Inject.html +16 -0
  98. package/docs/api/functions/index.NumberConverter.html +5 -0
  99. package/docs/api/functions/index.RegisterValidator.html +7 -0
  100. package/docs/api/functions/index.applyPipes.html +17 -0
  101. package/docs/api/functions/index.asyncHandler.html +11 -0
  102. package/docs/api/functions/index.capitalizePipe.html +4 -0
  103. package/docs/api/functions/index.clearPendingNavigations.html +1 -0
  104. package/docs/api/functions/index.compileTemplate.html +26 -0
  105. package/docs/api/functions/index.configure.html +5 -0
  106. package/docs/api/functions/index.createBluePrint.html +1 -0
  107. package/docs/api/functions/index.createConverterFromDataType.html +4 -0
  108. package/docs/api/functions/index.createConverterFromInputType.html +5 -0
  109. package/docs/api/functions/index.createPipeRegistry.html +12 -0
  110. package/docs/api/functions/index.currencyPipe.html +9 -0
  111. package/docs/api/functions/index.datePipe.html +9 -0
  112. package/docs/api/functions/index.daysAgoPipe.html +8 -0
  113. package/docs/api/functions/index.defaultPipe.html +5 -0
  114. package/docs/api/functions/index.defineRoutes.html +8 -0
  115. package/docs/api/functions/index.del.html +8 -0
  116. package/docs/api/functions/index.findRouteByName.html +5 -0
  117. package/docs/api/functions/index.findRouteByUrl.html +4 -0
  118. package/docs/api/functions/index.firstPipe.html +4 -0
  119. package/docs/api/functions/index.generateSequentialId.html +21 -0
  120. package/docs/api/functions/index.get.html +9 -0
  121. package/docs/api/functions/index.getDataConverter.html +11 -0
  122. package/docs/api/functions/index.getParentComponent.html +18 -0
  123. package/docs/api/functions/index.getValidator.html +4 -0
  124. package/docs/api/functions/index.html.html +19 -0
  125. package/docs/api/functions/index.joinPipe.html +5 -0
  126. package/docs/api/functions/index.keysPipe.html +4 -0
  127. package/docs/api/functions/index.lastPipe.html +4 -0
  128. package/docs/api/functions/index.lowercasePipe.html +4 -0
  129. package/docs/api/functions/index.mapFormToClass.html +17 -0
  130. package/docs/api/functions/index.matchRoute.html +5 -0
  131. package/docs/api/functions/index.navigate.html +8 -0
  132. package/docs/api/functions/index.onError.html +8 -0
  133. package/docs/api/functions/index.piecesPipe.html +8 -0
  134. package/docs/api/functions/index.post.html +9 -0
  135. package/docs/api/functions/index.printRoutes.html +2 -0
  136. package/docs/api/functions/index.put.html +9 -0
  137. package/docs/api/functions/index.readData.html +17 -0
  138. package/docs/api/functions/index.registerRouteTarget.html +9 -0
  139. package/docs/api/functions/index.reportError.html +10 -0
  140. package/docs/api/functions/index.request.html +8 -0
  141. package/docs/api/functions/index.resolveValue.html +18 -0
  142. package/docs/api/functions/index.setFetch.html +6 -0
  143. package/docs/api/functions/index.setFormData.html +17 -0
  144. package/docs/api/functions/index.shortenPipe.html +5 -0
  145. package/docs/api/functions/index.startRouting.html +6 -0
  146. package/docs/api/functions/index.ternaryPipe.html +6 -0
  147. package/docs/api/functions/index.trimPipe.html +4 -0
  148. package/docs/api/functions/index.unregisterRouteTarget.html +3 -0
  149. package/docs/api/functions/index.uppercasePipe.html +4 -0
  150. package/docs/api/hierarchy.html +1 -0
  151. package/docs/api/index.html +323 -0
  152. package/docs/api/interfaces/http.SimpleDataEvent.html +3 -0
  153. package/docs/api/interfaces/http.WebSocketAbstraction.html +9 -0
  154. package/docs/api/interfaces/http.WebSocketCodec.html +4 -0
  155. package/docs/api/interfaces/http.WebSocketOptions.html +20 -0
  156. package/docs/api/interfaces/index.CompiledTemplate.html +10 -0
  157. package/docs/api/interfaces/index.DataLoader.html +19 -0
  158. package/docs/api/interfaces/index.EngineConfig.html +11 -0
  159. package/docs/api/interfaces/index.ErrorContext.html +4 -0
  160. package/docs/api/interfaces/index.FormReaderOptions.html +8 -0
  161. package/docs/api/interfaces/index.HttpOptions.html +16 -0
  162. package/docs/api/interfaces/index.HttpResponse.html +17 -0
  163. package/docs/api/interfaces/index.LoadRoute.html +7 -0
  164. package/docs/api/interfaces/index.NavigateOptions.html +7 -0
  165. package/docs/api/interfaces/index.PipeRegistry.html +12 -0
  166. package/docs/api/interfaces/index.RegistrationOptions.html +22 -0
  167. package/docs/api/interfaces/index.RenderTemplate.html +7 -0
  168. package/docs/api/interfaces/index.RequestOptions.html +11 -0
  169. package/docs/api/interfaces/index.Routable.html +10 -0
  170. package/docs/api/interfaces/index.Route.html +13 -0
  171. package/docs/api/interfaces/index.RouteGuard.html +2 -0
  172. package/docs/api/interfaces/index.RouteValue.html +6 -0
  173. package/docs/api/interfaces/index.SSEOptions.html +24 -0
  174. package/docs/api/interfaces/index.ValidationContext.html +8 -0
  175. package/docs/api/interfaces/index.ValidatorOptions.html +14 -0
  176. package/docs/api/media/Architecture.md +333 -0
  177. package/docs/api/media/DependencyInjection.md +277 -0
  178. package/docs/api/media/GettingStarted.md +231 -0
  179. package/docs/api/media/HttpClient.md +459 -0
  180. package/docs/api/media/Pipes.md +211 -0
  181. package/docs/api/media/Routing.md +332 -0
  182. package/docs/api/media/WhyRelaxjs.md +336 -0
  183. package/docs/api/media/forms.md +99 -0
  184. package/docs/api/media/html.md +175 -0
  185. package/docs/api/media/i18n.md +354 -0
  186. package/docs/api/media/utilities.md +143 -0
  187. package/docs/api/media/validation.md +351 -0
  188. package/docs/api/modules/collections_Index.html +1 -0
  189. package/docs/api/modules/di.html +1 -0
  190. package/docs/api/modules/elements.html +1 -0
  191. package/docs/api/modules/forms.html +1 -0
  192. package/docs/api/modules/html.html +1 -0
  193. package/docs/api/modules/http.html +1 -0
  194. package/docs/api/modules/i18n.html +1 -0
  195. package/docs/api/modules/index.html +1 -0
  196. package/docs/api/modules/routing.html +1 -0
  197. package/docs/api/modules/utils.html +1 -0
  198. package/docs/api/modules.html +1 -0
  199. package/docs/api/types/http.WebSocketFactory.html +2 -0
  200. package/docs/api/types/i18n.MessageFormatter.html +3 -0
  201. package/docs/api/types/i18n.MissingTranslationHandler.html +1 -0
  202. package/docs/api/types/index.Constructor.html +7 -0
  203. package/docs/api/types/index.ConverterFunc.html +2 -0
  204. package/docs/api/types/index.DataType.html +2 -0
  205. package/docs/api/types/index.InputType.html +2 -0
  206. package/docs/api/types/index.PipeFunction.html +6 -0
  207. package/docs/api/types/index.RouteData.html +1 -0
  208. package/docs/api/types/index.RouteMatchResult.html +9 -0
  209. package/docs/api/types/index.RouteParamType.html +1 -0
  210. package/docs/api/types/index.RouteSegmentType.html +2 -0
  211. package/docs/api/types/index.SSEEventFactory.html +5 -0
  212. package/docs/api/types/index.ServiceScope.html +10 -0
  213. package/docs/api/types/index.SortColumn.html +3 -0
  214. package/docs/api/variables/i18n.formatICU.html +3 -0
  215. package/docs/api/variables/index.container.html +6 -0
  216. package/docs/api/variables/index.defaultPipes.html +6 -0
  217. package/docs/api/variables/index.internalRoutes.html +1 -0
  218. package/docs/api/variables/index.serviceCollection.html +6 -0
  219. package/docs/api.json +93171 -0
  220. package/docs/elements/dom.md +102 -102
  221. package/docs/forms/creating-form-components.md +924 -924
  222. package/docs/forms/form-api.md +94 -94
  223. package/docs/forms/forms.md +99 -99
  224. package/docs/forms/patterns.md +311 -311
  225. package/docs/forms/reading-writing.md +365 -365
  226. package/docs/forms/validation.md +351 -351
  227. package/docs/html/TableRenderer.md +291 -291
  228. package/docs/html/html.md +175 -175
  229. package/docs/html/index.md +54 -54
  230. package/docs/html/template.md +422 -422
  231. package/docs/http/HttpClient.md +459 -459
  232. package/docs/http/ServerSentEvents.md +184 -184
  233. package/docs/http/index.md +109 -109
  234. package/docs/i18n/i18n.md +49 -4
  235. package/docs/i18n/intl-standard.md +178 -178
  236. package/docs/routing/RouteLink.md +98 -98
  237. package/docs/routing/Routing.md +332 -332
  238. package/docs/routing/layouts.md +207 -207
  239. package/docs/utilities.md +143 -143
  240. 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 'relaxjs/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 'relaxjs/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 'relaxjs/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` |
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` |