@reactables/forms 1.0.3 → 1.1.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 +7 -592
- package/dist/RxForm/RxForm.d.ts +1 -1
- package/dist/index.js +2 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,597 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
Reactive forms with [Reactables](https://github.com/reactables/reactables/tree/main/packages/core).
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
[See docs at https://reactables.github.io/reactables/guides/forms/](https://reactables.github.io/reactables/guides/forms/)
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
1. [Examples](#examples)
|
|
11
|
-
1. [Basic Form Group](#basic-form-group)
|
|
12
|
-
1. [Validation](#validation-example)
|
|
13
|
-
1. [Async Validation](#async-validation-example)
|
|
14
|
-
1. [Normalizing Values](#normalizing-values)
|
|
15
|
-
1. [Custom Reducer](#custom-reducer-example)
|
|
9
|
+
### Contact
|
|
16
10
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
1. [removeControl](#api-actions-remove-control)
|
|
23
|
-
1. [markControlAsPristine](#api-actions-mark-as-pristine)
|
|
24
|
-
1. [markControlAsTouched](#api-actions-mark-as-touched)
|
|
25
|
-
1. [markControlAsUntouched](#api-actions-mark-as-untouched)
|
|
26
|
-
1. [resetControl](#api-actions-resetControl)
|
|
27
|
-
1. [build](#api-build)
|
|
28
|
-
1. [RxFormOptions](#api-form-options)
|
|
29
|
-
1. [control](#api-control)
|
|
30
|
-
1. [group](#api-group)
|
|
31
|
-
1. [array](#api-array)
|
|
32
|
-
1. [Other Interfaces](#api-interfaces)
|
|
33
|
-
1. [Form](#api-form)
|
|
34
|
-
1. [FormControl](#api-form-control)
|
|
35
|
-
1. [ControlRef](#api-control-ref)
|
|
36
|
-
1. [FormErrors](#api-form-errors)
|
|
37
|
-
1. [ValidatorFn](#api-validator-fn)
|
|
38
|
-
1. [ValidatorAsyncFn](#api-validator-fn-async)
|
|
39
|
-
1. [FormReducers](#api-form-reducers)
|
|
40
|
-
1. [CustomReducers](#api-custom-reducers)
|
|
41
|
-
1. [BaseFormState](#api-base-form-state)
|
|
42
|
-
1. [BaseControl](#api-base-control)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
## Installation <a name="installation"></a>
|
|
46
|
-
|
|
47
|
-
Installation requires [RxJS](https://rxjs.dev/) to be present.
|
|
48
|
-
|
|
49
|
-
`npm i rxjs` (if not already installed)
|
|
50
|
-
|
|
51
|
-
`npm i @reactables/forms`
|
|
52
|
-
|
|
53
|
-
## Examples <a name="examples"></a>
|
|
54
|
-
|
|
55
|
-
### Basic Form Group <a name="basic-form-group"></a>
|
|
56
|
-
|
|
57
|
-
[See full example on StackBlitz](https://stackblitz.com/edit/github-qtpo1k-vm45ed?file=src%2Findex.js)
|
|
58
|
-
|
|
59
|
-
```typescript
|
|
60
|
-
import { control, build, group } from '@reactables/forms';
|
|
61
|
-
|
|
62
|
-
const [state$, actions] = build(
|
|
63
|
-
group({
|
|
64
|
-
controls: {
|
|
65
|
-
name: control(['']),
|
|
66
|
-
},
|
|
67
|
-
})
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
// Cache the DOM
|
|
71
|
-
const nameControlEl = document.getElementById('name-control');
|
|
72
|
-
|
|
73
|
-
// Bind Event Handlers
|
|
74
|
-
nameControlEl.oninput = ({ target: { value } }) => {
|
|
75
|
-
actions.updateValues({
|
|
76
|
-
controlRef: ['name'],
|
|
77
|
-
value,
|
|
78
|
-
});
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
nameControlEl.onblur = () => {
|
|
82
|
-
actions.markControlAsTouched({ controlRef: ['name'] });
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// Subscribe to state updates and bind to view.
|
|
86
|
-
state$.subscribe((state) => {
|
|
87
|
-
const { name } = state;
|
|
88
|
-
|
|
89
|
-
nameControlEl.value = name.value;
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### Validation <a name="validation-example"></a>
|
|
95
|
-
|
|
96
|
-
`@reactable/forms` only comes with 3 built in validators, `required`, `email` & `arrayNotEmpty`. The developer can implement their own `ValidatorFn`s and provide them when building the reactable.
|
|
97
|
-
|
|
98
|
-
[See full example on StackBlitz](https://stackblitz.com/edit/github-qtpo1k-f6tz82?file=src%2Findex.js)
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
import { control, build, group } from '@reactables/forms';
|
|
102
|
-
|
|
103
|
-
// Create Reactable
|
|
104
|
-
const rxForm = build(
|
|
105
|
-
group({
|
|
106
|
-
controls: {
|
|
107
|
-
donuts: control(['0', ['required', 'min4']]),
|
|
108
|
-
},
|
|
109
|
-
}),
|
|
110
|
-
{
|
|
111
|
-
providers: {
|
|
112
|
-
validators: {
|
|
113
|
-
min4: (value) => ({ min4: Number(value) < 4 }),
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
}
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
const [state$, actions] = rxForm;
|
|
120
|
-
|
|
121
|
-
// ...Cache the DOM and bind event handlers
|
|
122
|
-
|
|
123
|
-
// Subscribe to state updates and bind to view.
|
|
124
|
-
state$.subscribe((state) => {
|
|
125
|
-
const { donuts } = state;
|
|
126
|
-
|
|
127
|
-
donutControlEl.value = donuts.value;
|
|
128
|
-
|
|
129
|
-
const handleErrors = (el, show) => {
|
|
130
|
-
el.className = show ? 'form-error show' : 'form-error';
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
handleErrors(donuntMinOrderErrorEl, donuts.touched && donuts.errors.min4);
|
|
134
|
-
handleErrors(donuntRequiredErrorEl, donuts.touched && donuts.errors.required);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Async Validation <a name="async-validation-example"></a>
|
|
142
|
-
|
|
143
|
-
`FormControl`s have a `pending: boolean` state when their value changes and are awaiting the result from asynchronous validation.
|
|
144
|
-
|
|
145
|
-
[See full example on StackBlitz](https://stackblitz.com/edit/github-qtpo1k-wvznqm?file=src%2Findex.js)
|
|
146
|
-
|
|
147
|
-
```typescript
|
|
148
|
-
import { control, build, group } from '@reactables/forms';
|
|
149
|
-
import { of, switchMap, delay } from 'rxjs';
|
|
150
|
-
|
|
151
|
-
const [state$, actions] = build(
|
|
152
|
-
group({
|
|
153
|
-
controls: {
|
|
154
|
-
email: control(['', ['required', 'email'], ['blacklistedEmail']]),
|
|
155
|
-
},
|
|
156
|
-
}),
|
|
157
|
-
{
|
|
158
|
-
providers: {
|
|
159
|
-
asyncValidators: {
|
|
160
|
-
blacklistedEmail: (control$) =>
|
|
161
|
-
control$.pipe(
|
|
162
|
-
switchMap(({ value }) =>
|
|
163
|
-
of({
|
|
164
|
-
blacklistedEmail: value === 'already@taken.com',
|
|
165
|
-
}).pipe(delay(1000))
|
|
166
|
-
)
|
|
167
|
-
),
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
}
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
// ...Bind Event Handlers
|
|
174
|
-
|
|
175
|
-
// Subscribe to state updates and bind to view.
|
|
176
|
-
state$.subscribe((state) => {
|
|
177
|
-
const { email } = state;
|
|
178
|
-
|
|
179
|
-
emailControlEl.value = email.value;
|
|
180
|
-
|
|
181
|
-
const handleErrors = (el, show) => {
|
|
182
|
-
el.className = show ? 'form-error show' : 'form-error';
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
emailPendingEl.className = email.pending ? 'show' : '';
|
|
186
|
-
|
|
187
|
-
handleErrors(emailRequiredErrorEl, email.touched && email.errors.required);
|
|
188
|
-
handleErrors(emailAsyncErrorEl, email.errors.blacklistedEmail);
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### Normalize Values <a name="normalizing-values"></a>
|
|
195
|
-
|
|
196
|
-
User input for a `FormControl` leaf (i.e having no child controls) can be normalized via normalizer functions provided during form initialization.
|
|
197
|
-
|
|
198
|
-
[See full example on StackBlitz](https://stackblitz.com/edit/github-qtpo1k-frpncu?file=src%2findex.js)
|
|
199
|
-
|
|
200
|
-
```typescript
|
|
201
|
-
import { control, build, group } from '@reactables/forms';
|
|
202
|
-
|
|
203
|
-
export const normalizePhone = (value) => {
|
|
204
|
-
let input = value.replace(/\D/g, '').substring(0, 10); // First ten digits of input only
|
|
205
|
-
const areaCode = input.substring(0, 3);
|
|
206
|
-
const middle = input.substring(3, 6);
|
|
207
|
-
const last = input.substring(6, 10);
|
|
208
|
-
|
|
209
|
-
if (input.length > 6) {
|
|
210
|
-
input = `(${areaCode}) ${middle} - ${last}`;
|
|
211
|
-
} else if (input.length > 3) {
|
|
212
|
-
input = `(${areaCode}) ${middle}`;
|
|
213
|
-
} else if (input.length > 0) {
|
|
214
|
-
input = `(${areaCode}`;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
return input;
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
const rxForm = build(
|
|
221
|
-
group({
|
|
222
|
-
controls: {
|
|
223
|
-
phone: control({
|
|
224
|
-
initialValue: '',
|
|
225
|
-
normalizers: ['phone']
|
|
226
|
-
}),
|
|
227
|
-
},
|
|
228
|
-
}),
|
|
229
|
-
{
|
|
230
|
-
providers: {
|
|
231
|
-
normalizers: {
|
|
232
|
-
phone: normalizePhone,
|
|
233
|
-
},
|
|
234
|
-
},
|
|
235
|
-
}
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
// ... Bind event handlers and view
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
### Custom Reducer <a name="custom-reducer-example"></a>
|
|
242
|
-
|
|
243
|
-
You can declare [`CustomReducer`s](#api-custom-reducers) during form initialization to implement custom behaviour.
|
|
244
|
-
|
|
245
|
-
Below the form reactable will have a `doubleOrder` action method which can be called to double the order amount.
|
|
246
|
-
|
|
247
|
-
[See full example on StackBlitz](https://stackblitz.com/edit/github-qtpo1k-3qppus?file=src%2Findex.js)
|
|
248
|
-
|
|
249
|
-
```typescript
|
|
250
|
-
import { control, build, group } from '@reactables/forms';
|
|
251
|
-
|
|
252
|
-
const [state$, actions] = build(
|
|
253
|
-
group({
|
|
254
|
-
controls: {
|
|
255
|
-
donuts: control(['0', 'min4']),
|
|
256
|
-
},
|
|
257
|
-
}),
|
|
258
|
-
{
|
|
259
|
-
providers: {
|
|
260
|
-
validators: {
|
|
261
|
-
min4: (value) => ({ min4: Number(value) < 4 }),
|
|
262
|
-
},
|
|
263
|
-
},
|
|
264
|
-
reducers: {
|
|
265
|
-
doubleOrder: (formReducers, state) => {
|
|
266
|
-
/** Use built in Form Reducers for updating the form tree. **/
|
|
267
|
-
const { updateValues } = formReducers;
|
|
268
|
-
|
|
269
|
-
const orders = Number(state.form.donuts.value);
|
|
270
|
-
const value = (orders * 2).toString();
|
|
271
|
-
|
|
272
|
-
return updateValues(state, { controlRef: ['donuts'], value });
|
|
273
|
-
};,
|
|
274
|
-
},
|
|
275
|
-
}
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
// ... Bind event handlers and view
|
|
279
|
-
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
## API <a name="api"></a>
|
|
283
|
-
|
|
284
|
-
The API for building Reactable Forms inspired by [Angular FormBuilder](https://angular.io/api/forms/FormBuilder). It has been adapted to support Reactable features.
|
|
285
|
-
|
|
286
|
-
### `RxActions` <a name="api-actions"></a>
|
|
287
|
-
|
|
288
|
-
Actions available to trigger state changes on Reactable.
|
|
289
|
-
|
|
290
|
-
#### `updateValues` <a name="api-actions-update-values"></a>
|
|
291
|
-
|
|
292
|
-
Updates values of a [`FormControl`](#api-form-control). For form group and form arrays, updates will only occur if the specified descendant controls exists. Otherwise it will throw an error.
|
|
293
|
-
|
|
294
|
-
```typescript
|
|
295
|
-
type updateValues = <T>(payload: UpdateValuesPayload<T>) => void;
|
|
296
|
-
|
|
297
|
-
interface UpdateValuesPayload<T> {
|
|
298
|
-
value: T;
|
|
299
|
-
controlRef: ControlRef;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
#### `addControl` <a name="api-actions-add-control"></a>
|
|
305
|
-
|
|
306
|
-
Adds a control to a form group.
|
|
307
|
-
|
|
308
|
-
```typescript
|
|
309
|
-
type addControl = (payload: AddControlPayload) => void;
|
|
310
|
-
|
|
311
|
-
interface AddControlPayload {
|
|
312
|
-
config: AbstractControlConfig;
|
|
313
|
-
controlRef: ControlRef;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
#### `pushControl` <a name="api-actions-push-control"></a>
|
|
319
|
-
|
|
320
|
-
Pushes a control to a form array.
|
|
321
|
-
|
|
322
|
-
```typescript
|
|
323
|
-
type pushControl = (payload: PushControlPayload) => void;
|
|
324
|
-
|
|
325
|
-
interface PushControlPayload {
|
|
326
|
-
config: AbstractControlConfig;
|
|
327
|
-
controlRef: ControlRef;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
#### `removeControl` <a name="api-actions-remove-control"></a>
|
|
333
|
-
|
|
334
|
-
Removes a specified control from the form.
|
|
335
|
-
|
|
336
|
-
```typescript
|
|
337
|
-
type removeControl = (payload: ControlRef) => void;
|
|
338
|
-
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
#### `markControlAsPristine` <a name="api-actions-mark-as-pristine"></a>
|
|
342
|
-
|
|
343
|
-
Marks a control and all descendant controls as pristine.
|
|
344
|
-
|
|
345
|
-
```typescript
|
|
346
|
-
type markControlAsPristine = (payload: ControlRef) => void;
|
|
347
|
-
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
#### `markControlAsTouched` <a name="api-actions-mark-as-touched"></a>
|
|
351
|
-
|
|
352
|
-
Marks a control and all ancestors as touched. Can set `markAll` to `true` to mark all descendants as touched as well (defaults to `false`).
|
|
353
|
-
|
|
354
|
-
```typescript
|
|
355
|
-
type markControlAsTouched = (payload: MarkTouchedPayload) => void;
|
|
356
|
-
|
|
357
|
-
interface MarkTouchedPayload {
|
|
358
|
-
controlRef: ControlRef;
|
|
359
|
-
markAll?: boolean;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
#### `markControlAsUntouched` <a name="api-actions-mark-as-untouched"></a>
|
|
365
|
-
|
|
366
|
-
Marks a control and all descendants as untouched. This will recheck ancestor controls and update the touched status.
|
|
367
|
-
|
|
368
|
-
```typescript
|
|
369
|
-
type markControlAsUnTouched = (payload: ControlRef) => void;
|
|
370
|
-
|
|
371
|
-
```
|
|
372
|
-
|
|
373
|
-
#### `resetControl` <a name="api-actions-resetControl"></a>
|
|
374
|
-
|
|
375
|
-
Resets a control by removing existing control and rebuilding it with the original configuration.
|
|
376
|
-
|
|
377
|
-
```typescript
|
|
378
|
-
type resetControls = (payload: ControlRef) => void;
|
|
379
|
-
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
### `build` <a name="api-build"></a>
|
|
383
|
-
|
|
384
|
-
```typescript
|
|
385
|
-
type build = (config: AbstractControlConfig, options?: RxFormOptions) => Reactable<Form<unknown>, RxFormActions>
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
Factory method for creating a form Reactable. Accepts a configuration object generated by one or more helper methods - [`control`](#api-control), [`group`](#api-group), [`array`](#api-array). Also accepts an `RxFormOptions` object.
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
#### `RxFormOptions` <a name="api-form-options"></a>
|
|
392
|
-
|
|
393
|
-
Options to customize RxForm behaviour.
|
|
394
|
-
|
|
395
|
-
```typescript
|
|
396
|
-
interface RxFormOptions {
|
|
397
|
-
reducers?: { [key:string]: CustomReducer }
|
|
398
|
-
effects?: Effect<unknown, unknown>[];
|
|
399
|
-
sources?: Observable<Action<unknown>>[] | { [key: string]: Observable<unknown> };
|
|
400
|
-
}
|
|
401
|
-
```
|
|
402
|
-
| Property | Description |
|
|
403
|
-
| -------- | ----------- |
|
|
404
|
-
| reducers (optional) | Dictionary of [`CustomReducer`s](#api-custom-reducers) to implement custom form behaviour. The `CustomReducerFunc`(#api-custom-reducers) provides built in [`FormReducers`](#api-form-reducers). **Form state updates need to be made with [`FormReducers`](#api-form-reducers) to maintain integrity of the form state tree (i.e validation states of parent and child controls)**. |
|
|
405
|
-
| effects (optional) | Array of [Effects](https://github.com/reactables/reactables/tree/main/packages/core#api-effect) to be registered to the Reactable. |
|
|
406
|
-
| sources (optional) | Additional [Action](https://github.com/reactables/reactables/tree/main/packages/core#action-) Observables the Reactable is listening to. Can be an array or a dictionary where key is the action type and value is the Observable emitting the payload. |
|
|
407
|
-
|
|
408
|
-
### `control` <a name="api-control"></a>
|
|
409
|
-
|
|
410
|
-
Function to create a `FormControlConfig` configuration object. Accepts a configuration object or a tuple.
|
|
411
|
-
```typescript
|
|
412
|
-
type control = <T>(config: FormControlConfig<T> | FbControl<T>) => FormControlConfig<T>
|
|
413
|
-
|
|
414
|
-
interface FormControlConfig<T> {
|
|
415
|
-
initialValue: T;
|
|
416
|
-
validators?: ValidatorFn[];
|
|
417
|
-
asyncValidators?: ValidatorAsyncFn[];
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
type FbControl<T> = [T, (ValidatorFn | ValidatorFn[])?, (ValidatorAsyncFn | ValidatorAsyncFn[])?];
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
### `group` <a name="api-group"></a>
|
|
424
|
-
|
|
425
|
-
Function to create a `FormGroupConfig` configuration object. Accepts a configuration object containing a `controls` dictionary of additional configuration objects generated by [`control`](#api-control), [`group`](#api-group), or [`array`](#api-array).
|
|
426
|
-
|
|
427
|
-
```typescript
|
|
428
|
-
type group = (config: FormGroupConfig) => FormGroupConfig
|
|
429
|
-
|
|
430
|
-
interface FormGroupConfig{
|
|
431
|
-
validators?: ValidatorFn[];
|
|
432
|
-
asyncValidators?: ValidatorAsyncFn[];
|
|
433
|
-
controls: { [key: string]: AbstractControlConfig };
|
|
434
|
-
}
|
|
435
|
-
```
|
|
436
|
-
### `array` <a name="api-array"></a>
|
|
437
|
-
|
|
438
|
-
Function to create a `FormArrayConfig` configuration object. Accepts a configuration object containing a `controls` array of additional configuration objects generated by [`control`](#api-control), [`group`](#api-group), or [`array`](#api-array).
|
|
439
|
-
|
|
440
|
-
```typescript
|
|
441
|
-
type array = (config: FormArrayConfig) => FormArrayConfig
|
|
442
|
-
|
|
443
|
-
interface FormArrayConfig {
|
|
444
|
-
validators?: ValidatorFn[];
|
|
445
|
-
asyncValidators?: ValidatorAsyncFn[];
|
|
446
|
-
controls: AbstractControlConfig[];
|
|
447
|
-
}
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
### Other Interfaces <a name="api-interfaces"></a>
|
|
451
|
-
|
|
452
|
-
#### `Form` <a name="api-form"></a>
|
|
453
|
-
Form state. Dictionary of [`FormControl`](#api-form-control)(s) where the key is a period separated representation of the [`ControlRef`](#api-control-ref) tuple.
|
|
454
|
-
|
|
455
|
-
```typescript
|
|
456
|
-
interface Form<T> {
|
|
457
|
-
root?: FormControl<T>;
|
|
458
|
-
[key: string]: FormControl<unknown>;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
#### `FormControl` <a name="api-form-control"></a>
|
|
464
|
-
|
|
465
|
-
```typescript
|
|
466
|
-
|
|
467
|
-
interface FormControl<T> {
|
|
468
|
-
pristineValue: T;
|
|
469
|
-
controlRef: ControlRef;
|
|
470
|
-
value: T;
|
|
471
|
-
dirty: boolean;
|
|
472
|
-
touched: boolean;
|
|
473
|
-
validatorErrors: FormErrors;
|
|
474
|
-
key: string;
|
|
475
|
-
asyncValidatorErrors: FormErrors;
|
|
476
|
-
asyncValidateInProgress: { [key: string | number]: boolean };
|
|
477
|
-
errors: FormErrors;
|
|
478
|
-
valid: boolean;
|
|
479
|
-
childrenValid: boolean;
|
|
480
|
-
pending?: boolean;
|
|
481
|
-
config: AbstractControlConfig;
|
|
482
|
-
}
|
|
483
|
-
```
|
|
484
|
-
| Property | Description |
|
|
485
|
-
| -------- | ----------- |
|
|
486
|
-
| pristineValue | Original value of control. Use to determine if control is dirty. |
|
|
487
|
-
| controlRef | Controls [`ControlRef`](#api-control-ref). |
|
|
488
|
-
| value | Control value. |
|
|
489
|
-
| touched | Touched status of control |
|
|
490
|
-
| validatorErrors | [`FormErrors`](#api-form-errors) from validators (non-async) |
|
|
491
|
-
| asyncValidatorErrors | [`FormErrors`](#api-form-errors) from async validators |
|
|
492
|
-
| errors | [`FormErrors`](#api-form-errors) validatorErrors and asyncValidatorErrors merged. |
|
|
493
|
-
| valid | Valid status of control. Also checks descendants.
|
|
494
|
-
| childrenValid | Valid status of direct child controls.
|
|
495
|
-
| config | Original config for form control |
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
#### `ControlRef` <a name="api-control-ref"></a>
|
|
500
|
-
|
|
501
|
-
Control Reference represented as a tuple for the [`FormControl`](#api-form-control)
|
|
502
|
-
|
|
503
|
-
#### `FormErrors` <a name="api-form-errors"></a>
|
|
504
|
-
|
|
505
|
-
Dictionary of errors for the control.
|
|
506
|
-
|
|
507
|
-
```typescript
|
|
508
|
-
interface FormErrors {
|
|
509
|
-
[key: string]: boolean;
|
|
510
|
-
}
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
#### `ValidatorFn` <a name="api-validator-fn"></a>
|
|
514
|
-
|
|
515
|
-
Validator function that reads the value of the `FormControl` and returns a `FormErrors` object.
|
|
516
|
-
|
|
517
|
-
```typescript
|
|
518
|
-
type ValidatorFn = (value: unknown) => FormErrors
|
|
519
|
-
```
|
|
520
|
-
|
|
521
|
-
#### `ValidatorFnAsync` <a name="api-validator-fn-async"></a>
|
|
522
|
-
|
|
523
|
-
Validator function takes in an `BaseControl` observable and returns an `Observable<FormErrors>`.
|
|
524
|
-
|
|
525
|
-
```typescript
|
|
526
|
-
type ValidatorAsyncFn = <T>(control$: Observable<BaseControl<T>>) => Observable<FormErrors>;
|
|
527
|
-
|
|
528
|
-
```
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
#### `FormReducers` <a name="api-form-reducers"></a>
|
|
532
|
-
|
|
533
|
-
Built in reducers which can be used to update the state of the form tree. Payload and behaviour is the same and described in [`RxActions`](#api-actions);
|
|
534
|
-
|
|
535
|
-
```typescript
|
|
536
|
-
|
|
537
|
-
interface FormReducers {
|
|
538
|
-
updateValues: <T>(state: BaseFormState<T>, payload: UpdateValuesPayload<unknown>,
|
|
539
|
-
) => BaseFormState<T>;
|
|
540
|
-
removeControl: <T>(state: BaseFormState<T>, payload: ControlRef) => BaseFormState<T>;
|
|
541
|
-
pushControl: <T>(state: BaseFormState<T>, payload: PushControlPayload) => BaseFormState<T>;
|
|
542
|
-
addControl: <T>(state: BaseFormState<T>, payload: AddControlPayload) => BaseFormState<T>;
|
|
543
|
-
markControlAsPristine: <T>(state: BaseFormState<T>, payload: ControlRef) => BaseFormState<T>;
|
|
544
|
-
markControlAsTouched: <T>(state: BaseFormState<T>, payload: MarkTouchedPayload) => BaseFormState<T>;
|
|
545
|
-
markControlAsUntouched: <T>(state: BaseFormState<T>, payload: ControlRef,
|
|
546
|
-
) => BaseFormState<T>;
|
|
547
|
-
resetControl: <T>(state: BaseFormState<T>, payload: ControlRef) => BaseFormState<T>;
|
|
548
|
-
}
|
|
549
|
-
```
|
|
550
|
-
#### `CustomReducers` <a name="api-custom-reducers"></a>
|
|
551
|
-
|
|
552
|
-
```typescript
|
|
553
|
-
type CustomReducerFunc = (
|
|
554
|
-
reducers: FormReducers,
|
|
555
|
-
state: BaseFormState<unknown>,
|
|
556
|
-
action: Action<unknown>,
|
|
557
|
-
) => BaseFormState<unknown>;
|
|
558
|
-
|
|
559
|
-
type CustomReducer =
|
|
560
|
-
| CustomReducerFunc
|
|
561
|
-
| {
|
|
562
|
-
reducer: CustomReducerFunc;
|
|
563
|
-
effects?: Effect<unknown, unknown>[] | ((payload?: unknown) => ScopedEffects<unknown>);
|
|
564
|
-
};
|
|
565
|
-
|
|
566
|
-
```
|
|
567
|
-
|
|
568
|
-
#### `BaseFormState` <a name="api-base-form-state"></a>
|
|
569
|
-
|
|
570
|
-
Form state before it is fully validated. This is accessible in `CustomReducer`s so developer can read the current state and implement custom form behaviours.
|
|
571
|
-
|
|
572
|
-
```typescript
|
|
573
|
-
interface BaseFormState<T> {
|
|
574
|
-
form: BaseForm<T>;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
interface BaseForm<T> {
|
|
578
|
-
root?: BaseControl<T>;
|
|
579
|
-
[key: string]: BaseControl<unknown>;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
```
|
|
583
|
-
|
|
584
|
-
#### `BaseControl` <a name="api-base-control"></a>
|
|
585
|
-
|
|
586
|
-
`BaseControl` contains some control information before a fully validated `FormControl` is created.
|
|
587
|
-
|
|
588
|
-
```typescript
|
|
589
|
-
|
|
590
|
-
interface BaseControl<T> {
|
|
591
|
-
pristineValue: T;
|
|
592
|
-
controlRef: ControlRef;
|
|
593
|
-
value: T;
|
|
594
|
-
dirty: boolean;
|
|
595
|
-
touched: boolean;
|
|
596
|
-
validatorErrors: FormErrors;
|
|
597
|
-
config: AbstractControlConfig;
|
|
598
|
-
key: string;
|
|
599
|
-
}
|
|
600
|
-
```
|
|
11
|
+
Dave Lai
|
|
12
|
+
email: <a href="dlai@dave-lai.com">dlai@dave-lai.com</a>
|
|
13
|
+
<br>
|
|
14
|
+
<br>
|
|
15
|
+
Github: https://github.com/laidav
|
package/dist/RxForm/RxForm.d.ts
CHANGED
|
@@ -58,5 +58,5 @@ export interface RxFormProviders {
|
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
export declare const build: <Value, Actions = RxFormActions>(config: AbstractControlConfig, options?: RxFormOptions) => Reactable<Form<Value>, Actions>;
|
|
61
|
-
export declare const load: <Value, Actions = RxFormActions>(state: Form<
|
|
61
|
+
export declare const load: <Value, Actions = RxFormActions>(state: Form<Value>, options?: RxFormOptions) => Reactable<Form<Value>, Actions>;
|
|
62
62
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -1162,7 +1162,7 @@ var createReactable = function (initialBaseState, options, initialFormState) {
|
|
|
1162
1162
|
return pushControl(state, action, providers, mergeChanges);
|
|
1163
1163
|
}, resetControl: function (state, action, mergeChanges) {
|
|
1164
1164
|
return resetControl(state, action, providers, mergeChanges);
|
|
1165
|
-
}, markControlAsPristine: markControlAsPristine, markControlAsTouched: markControlAsTouched, markControlAsUntouched: markControlAsUntouched }, customReducers) }, otherOptions)), hub1State$ = _d[0], hub1Actions = _d[1];
|
|
1165
|
+
}, markControlAsPristine: markControlAsPristine, markControlAsTouched: markControlAsTouched, markControlAsUntouched: markControlAsUntouched }, customReducers) }, otherOptions)), hub1State$ = _d[0], hub1Actions = _d[1], hub1Actions$ = _d[2];
|
|
1166
1166
|
var state$ = core.RxBuilder({
|
|
1167
1167
|
sources: [buildHub2Source(hub1State$, initialBaseState).pipe(operators.skip(initialFormState ? 1 : 0))],
|
|
1168
1168
|
initialState: initialFormState || null,
|
|
@@ -1184,6 +1184,7 @@ var createReactable = function (initialBaseState, options, initialFormState) {
|
|
|
1184
1184
|
return [
|
|
1185
1185
|
state$.pipe(operators.filter(function (form) { return form !== null; })),
|
|
1186
1186
|
hub1Actions,
|
|
1187
|
+
hub1Actions$,
|
|
1187
1188
|
];
|
|
1188
1189
|
};
|
|
1189
1190
|
|
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"author": "David Lai",
|
|
15
15
|
"license": "ISC",
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@reactables/core": "^1.0
|
|
17
|
+
"@reactables/core": "^1.1.0",
|
|
18
18
|
"lodash.isequal": "^4.5.0"
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
@@ -23,5 +23,5 @@
|
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"lodash.clonedeep": "^4.5.0"
|
|
25
25
|
},
|
|
26
|
-
"version": "1.0
|
|
26
|
+
"version": "1.1.0"
|
|
27
27
|
}
|