@samline/forms 2.7.2 → 2.8.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/CHANGELOG.md +302 -0
- package/README.md +110 -122
- package/dist/browser/global.d.ts +1 -13
- package/dist/browser/global.global.js +12 -6188
- package/dist/browser/global.global.js.map +1 -1
- package/dist/browser/index.cjs +125 -56
- package/dist/browser/index.cjs.map +1 -1
- package/dist/browser/index.js +1 -1
- package/dist/{chunk-MMHX34WB.js → chunk-23VZAYA3.js} +126 -57
- package/dist/chunk-23VZAYA3.js.map +1 -0
- package/dist/index.cjs +125 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +35 -7
- package/dist/chunk-MMHX34WB.js.map +0 -1
- package/docs/api/add-cleanup.md +0 -52
- package/docs/api/append.md +0 -96
- package/docs/api/auto-submit.md +0 -91
- package/docs/api/clear-errors.md +0 -69
- package/docs/api/destroy.md +0 -80
- package/docs/api/disable-auto-submit.md +0 -65
- package/docs/api/element.md +0 -78
- package/docs/api/form.md +0 -156
- package/docs/api/format.md +0 -270
- package/docs/api/get-data.md +0 -90
- package/docs/api/get-field.md +0 -87
- package/docs/api/get-state.md +0 -111
- package/docs/api/get-value.md +0 -114
- package/docs/api/index.md +0 -55
- package/docs/api/observe.md +0 -126
- package/docs/api/on-submit.md +0 -94
- package/docs/api/parse-form-data.md +0 -94
- package/docs/api/prefill.md +0 -89
- package/docs/api/regex.md +0 -155
- package/docs/api/reset.md +0 -61
- package/docs/api/revalidate.md +0 -52
- package/docs/api/set-errors.md +0 -97
- package/docs/api/set-value.md +0 -120
- package/docs/api/subscribe.md +0 -107
- package/docs/api/unwatch.md +0 -74
- package/docs/api/validate-field-value.md +0 -144
- package/docs/api/validate-values.md +0 -103
- package/docs/api/validate.md +0 -102
- package/docs/api/watch.md +0 -117
- package/docs/browser.md +0 -156
- package/docs/css-styling.md +0 -191
- package/docs/getting-started.md +0 -222
- package/docs/options.md +0 -326
- package/docs/recipes.md +0 -531
- package/docs/typescript.md +0 -615
- package/docs/vanilla.md +0 -12
- /package/{LICENSE → LICENSE.md} +0 -0
package/docs/api/clear-errors.md
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
# `clearErrors(fields?)`
|
|
2
|
-
|
|
3
|
-
Removes manual errors. Validation errors from rules are not touched.
|
|
4
|
-
|
|
5
|
-
## Signature
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
clearErrors(): FormController
|
|
9
|
-
clearErrors(fields: string[]): FormController
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
## Parameters
|
|
13
|
-
|
|
14
|
-
| Name | Type | Required | Description |
|
|
15
|
-
| --- | --- | --- | --- |
|
|
16
|
-
| `fields` | `string[]` | no | When provided, only the listed fields are cleared. When omitted, every manual error is cleared. |
|
|
17
|
-
|
|
18
|
-
## Returns
|
|
19
|
-
|
|
20
|
-
The same [`FormController`](../typescript.md#formcontroller) — chainable.
|
|
21
|
-
|
|
22
|
-
## Behaviour
|
|
23
|
-
|
|
24
|
-
- Only manual errors (set via [`setErrors`](set-errors.md)) are removed. Validation errors from rules are recomputed the next time validation runs.
|
|
25
|
-
- Visual attributes are re-synced for the affected fields — `css-error` is removed when no errors remain for a field.
|
|
26
|
-
- Subscribers are notified.
|
|
27
|
-
|
|
28
|
-
## Examples
|
|
29
|
-
|
|
30
|
-
### Clear a single field
|
|
31
|
-
|
|
32
|
-
```ts
|
|
33
|
-
import { form } from '@samline/forms'
|
|
34
|
-
|
|
35
|
-
const profile = form('profile-form')
|
|
36
|
-
|
|
37
|
-
profile.setErrors({ email: ['Already in use.'] })
|
|
38
|
-
|
|
39
|
-
profile.clearErrors(['email'])
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### Clear everything
|
|
43
|
-
|
|
44
|
-
```ts
|
|
45
|
-
profile.setErrors({
|
|
46
|
-
email: ['Already in use.'],
|
|
47
|
-
password: ['Too weak.']
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
profile.clearErrors()
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### Clear before re-running validation
|
|
54
|
-
|
|
55
|
-
```ts
|
|
56
|
-
profile.clearErrors()
|
|
57
|
-
profile.validate()
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## Edge cases
|
|
61
|
-
|
|
62
|
-
- **Validation errors are not cleared by `clearErrors`.** They live in a separate map and are only cleared by [`reset`](reset.md) or by a successful re-validation of the field.
|
|
63
|
-
- **`clearErrors` on a field with no manual error is a no-op** — the chain still returns the controller.
|
|
64
|
-
- **Passing an empty array `clearErrors([])` does nothing.** Use `clearErrors()` to clear everything.
|
|
65
|
-
|
|
66
|
-
## Related
|
|
67
|
-
|
|
68
|
-
- [`setErrors`](set-errors.md) — push manual errors.
|
|
69
|
-
- [`reset`](reset.md) — clear every error and reset field values.
|
package/docs/api/destroy.md
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
# `destroy()`
|
|
2
|
-
|
|
3
|
-
Tears the controller down. Removes all DOM listeners, disconnects the `MutationObserver`, runs registered cleanups, clears caches, drops subscribers and submit handlers, and resets internal state.
|
|
4
|
-
|
|
5
|
-
## Signature
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
destroy(): void
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Returns
|
|
12
|
-
|
|
13
|
-
`void`. Idempotent — calling `destroy()` more than once is a no-op.
|
|
14
|
-
|
|
15
|
-
## Behaviour
|
|
16
|
-
|
|
17
|
-
When invoked, the controller:
|
|
18
|
-
|
|
19
|
-
- Removes every listener it added (`input`, `submit`, formatter listeners, and document delegation for external controls).
|
|
20
|
-
- Cancels any pending auto-submit debounce timer.
|
|
21
|
-
- Disconnects the form `MutationObserver`.
|
|
22
|
-
- Runs active callbacks registered with [`addCleanup()`](add-cleanup.md) once in reverse registration order. A thrown cleanup is logged and does not stop the others.
|
|
23
|
-
- Clears `watchedFields`, `subscribers`, `submitHandlers`, and `fieldCache`.
|
|
24
|
-
- Resets manual and validation error maps.
|
|
25
|
-
- Clears tracked async-submit state. Pending handler promises continue independently, but no completion notification is emitted after destruction.
|
|
26
|
-
|
|
27
|
-
After `destroy()` the controller object still exists and its methods remain callable. Direct reads and writes can still inspect or mutate the bound DOM, but the removed controller listeners no longer react to resulting events.
|
|
28
|
-
|
|
29
|
-
## Examples
|
|
30
|
-
|
|
31
|
-
### SPA route unmount
|
|
32
|
-
|
|
33
|
-
```ts
|
|
34
|
-
import { form } from '@samline/forms'
|
|
35
|
-
|
|
36
|
-
let profile: ReturnType<typeof form> | null = null
|
|
37
|
-
|
|
38
|
-
function mountProfileForm(element: HTMLFormElement) {
|
|
39
|
-
profile = form(element, { validators: { email: { required: true } } })
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function unmountProfileForm() {
|
|
43
|
-
profile?.destroy()
|
|
44
|
-
profile = null
|
|
45
|
-
}
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
### Cleanup on script teardown
|
|
49
|
-
|
|
50
|
-
```ts
|
|
51
|
-
const profile = form('profile-form')
|
|
52
|
-
|
|
53
|
-
window.addEventListener('beforeunload', () => {
|
|
54
|
-
profile.destroy()
|
|
55
|
-
})
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### Own integration resources
|
|
59
|
-
|
|
60
|
-
```ts
|
|
61
|
-
const profile = form('profile-form')
|
|
62
|
-
const observer = new ResizeObserver(() => updateLayout())
|
|
63
|
-
|
|
64
|
-
if (profile.element) observer.observe(profile.element)
|
|
65
|
-
profile.addCleanup(() => observer.disconnect())
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## Edge cases
|
|
69
|
-
|
|
70
|
-
- **`destroy()` does not remove `css-filled` / `css-error` attributes** from fields. If you need that, call [`reset()`](reset.md) before destroy, or strip the attributes yourself.
|
|
71
|
-
- **Subscribers and watchers stop receiving notifications immediately**, but their closures remain until you release them.
|
|
72
|
-
- **Cleanup registrations are independent.** Registering the same callback twice runs it twice unless one registration's unregister function is called.
|
|
73
|
-
- **Late cleanup registration runs immediately.** Calling `addCleanup()` after destruction executes the callback at once and returns a no-op unregister function.
|
|
74
|
-
- **Reusing the controller reference after destroy is possible but usually misleading.** Reads still inspect the bound form and methods such as `setValue()` can still write fields, but watchers, subscribers, validation-on-input, and auto-submit no longer run.
|
|
75
|
-
|
|
76
|
-
## Related
|
|
77
|
-
|
|
78
|
-
- [`reset`](reset.md) — restore form values without removing listeners.
|
|
79
|
-
- [`form`](form.md) — bind a new controller.
|
|
80
|
-
- [`addCleanup`](add-cleanup.md) — register controller-owned teardown work.
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# `disableAutoSubmit()`
|
|
2
|
-
|
|
3
|
-
Turns auto-submit off and cancels any pending debounce timer.
|
|
4
|
-
|
|
5
|
-
## Signature
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
disableAutoSubmit(): FormController
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Returns
|
|
12
|
-
|
|
13
|
-
The same [`FormController`](../typescript.md#formcontroller) — chainable.
|
|
14
|
-
|
|
15
|
-
## Behaviour
|
|
16
|
-
|
|
17
|
-
- Sets `state.autoSubmitEnabled` to `false`.
|
|
18
|
-
- Cancels `state.autoSubmitTimer` if one is pending.
|
|
19
|
-
- Notifies subscribers (because the state’s `autoSubmit` field changes).
|
|
20
|
-
|
|
21
|
-
Equivalent to calling [`autoSubmit(false)`](auto-submit.md).
|
|
22
|
-
|
|
23
|
-
## Examples
|
|
24
|
-
|
|
25
|
-
### Pause autosave while typing resumes
|
|
26
|
-
|
|
27
|
-
```ts
|
|
28
|
-
import { form } from '@samline/forms'
|
|
29
|
-
|
|
30
|
-
const draft = form('draft-form', { autoSubmit: { debounce: 800 } })
|
|
31
|
-
|
|
32
|
-
document.querySelector('#pause')?.addEventListener('click', () => {
|
|
33
|
-
draft.disableAutoSubmit()
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
document.querySelector('#resume')?.addEventListener('click', () => {
|
|
37
|
-
draft.autoSubmit({ debounce: 800 })
|
|
38
|
-
})
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### Disable on blur
|
|
42
|
-
|
|
43
|
-
```ts
|
|
44
|
-
import { form } from '@samline/forms'
|
|
45
|
-
|
|
46
|
-
const search = form('search-form', { autoSubmit: { debounce: 250 } })
|
|
47
|
-
|
|
48
|
-
search.element?.addEventListener('focusin', () => {
|
|
49
|
-
search.autoSubmit({ debounce: 250 })
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
search.element?.addEventListener('focusout', () => {
|
|
53
|
-
search.disableAutoSubmit()
|
|
54
|
-
})
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
## Edge cases
|
|
58
|
-
|
|
59
|
-
- **Re-enabling later requires calling [`autoSubmit()`](auto-submit.md) again.** The previous debounce configuration is not preserved.
|
|
60
|
-
- **A pending debounce timer is removed**, but the timer was already scheduled; nothing fires for it.
|
|
61
|
-
|
|
62
|
-
## Related
|
|
63
|
-
|
|
64
|
-
- [`autoSubmit`](auto-submit.md) — enable / configure.
|
|
65
|
-
- [`destroy`](destroy.md) — also cancels any pending debounce.
|
package/docs/api/element.md
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
# `element` and `f`
|
|
2
|
-
|
|
3
|
-
Read-only getters that return the bound `HTMLFormElement`, or `null` if the binding target was unresolved at construction time.
|
|
4
|
-
|
|
5
|
-
## Signatures
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
interface FormController {
|
|
9
|
-
readonly element: HTMLFormElement | null
|
|
10
|
-
readonly f: HTMLFormElement | null
|
|
11
|
-
readonly options: FormControllerOptions
|
|
12
|
-
}
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
- `element` — the bound form.
|
|
16
|
-
- `f` — alias of `element`, kept short for fluent setup.
|
|
17
|
-
- `options` — the resolved options object (defaults merged with user-provided values).
|
|
18
|
-
|
|
19
|
-
## Returns
|
|
20
|
-
|
|
21
|
-
`HTMLFormElement | null`. `null` when the target passed to [`form()`](form.md) did not resolve to a form.
|
|
22
|
-
|
|
23
|
-
## Examples
|
|
24
|
-
|
|
25
|
-
### Read the bound form
|
|
26
|
-
|
|
27
|
-
```ts
|
|
28
|
-
import { form } from '@samline/forms'
|
|
29
|
-
|
|
30
|
-
const profile = form('profile-form')
|
|
31
|
-
|
|
32
|
-
if (profile.element) {
|
|
33
|
-
profile.element.classList.add('mounted')
|
|
34
|
-
}
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### Use the short alias
|
|
38
|
-
|
|
39
|
-
```ts
|
|
40
|
-
import { form } from '@samline/forms'
|
|
41
|
-
|
|
42
|
-
const profile = form('profile-form')
|
|
43
|
-
|
|
44
|
-
profile.f?.querySelector('[data-status]')?.setAttribute('data-state', 'ready')
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### Read the resolved options
|
|
48
|
-
|
|
49
|
-
```ts
|
|
50
|
-
import { form } from '@samline/forms'
|
|
51
|
-
|
|
52
|
-
const profile = form('profile-form', {
|
|
53
|
-
autoValidate: false,
|
|
54
|
-
attributes: { error: 'is-invalid' },
|
|
55
|
-
formats: {
|
|
56
|
-
phone: { type: 'phone', field: 'phone', options: { country: 'MX' } }
|
|
57
|
-
}
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
profile.options.autoValidate // false (overridden)
|
|
61
|
-
profile.options.autoSubmit // false (default)
|
|
62
|
-
profile.options.clearErrorsOnSubmit // true (default)
|
|
63
|
-
profile.options.attributes.error // 'is-invalid'
|
|
64
|
-
profile.options.attributes.filled // 'css-filled' (default)
|
|
65
|
-
profile.options.formats.phone // FieldFormatConfig (overridden)
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
The `formats` key is the declarative counterpart to [`format()`](format.md) — see [the option reference](../options.md#formats) for the full shape.
|
|
69
|
-
|
|
70
|
-
## Edge cases
|
|
71
|
-
|
|
72
|
-
- The getter returns `null` when the target was `null`, `undefined`, a non-existent id, or an element that is not an `HTMLFormElement`.
|
|
73
|
-
- The reference is the same one the controller uses internally — do not replace the form node, or the controller will keep listening to the original.
|
|
74
|
-
|
|
75
|
-
## Related
|
|
76
|
-
|
|
77
|
-
- [`form(target, options?)`](form.md) — bind a controller.
|
|
78
|
-
- [`destroy`](destroy.md) — tear the controller down.
|
package/docs/api/form.md
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
# `form(target, options?)`
|
|
2
|
-
|
|
3
|
-
Creates a new controller bound to a form. This is the main entry point of `@samline/forms`.
|
|
4
|
-
|
|
5
|
-
## Signature
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
function form(
|
|
9
|
-
target: FormTarget,
|
|
10
|
-
options?: FormControllerOptions
|
|
11
|
-
): FormController
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## Parameters
|
|
15
|
-
|
|
16
|
-
| Name | Type | Required | Description |
|
|
17
|
-
| --- | --- | --- | --- |
|
|
18
|
-
| `target` | [`FormTarget`](../typescript.md#formtarget) | yes | What to bind to: a string id, an `HTMLFormElement`, a ref-like `{ current }` object, or `null` / `undefined`. |
|
|
19
|
-
| `options` | [`FormControllerOptions`](../typescript.md#formcontrolleroptions) | no | Controller configuration. See [docs/options.md](../options.md). |
|
|
20
|
-
|
|
21
|
-
## Returns
|
|
22
|
-
|
|
23
|
-
A [`FormController`](../typescript.md#formcontroller) — chainable for every method that does not return data or an unsubscribe function.
|
|
24
|
-
|
|
25
|
-
## Binding modes
|
|
26
|
-
|
|
27
|
-
### By id string
|
|
28
|
-
|
|
29
|
-
```ts
|
|
30
|
-
import { form } from '@samline/forms'
|
|
31
|
-
|
|
32
|
-
const profile = form('profile-form')
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
The string is passed to `document.getElementById`. If no element is found, or the found element is not an `HTMLFormElement`, the controller is created but `controller.element` is `null` and most methods become no-ops.
|
|
36
|
-
|
|
37
|
-
### By `HTMLFormElement`
|
|
38
|
-
|
|
39
|
-
```ts
|
|
40
|
-
const element = document.querySelector<HTMLFormElement>('#profile-form')
|
|
41
|
-
const profile = form(element)
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Useful when you already have the element from a `querySelector` call.
|
|
45
|
-
|
|
46
|
-
### By ref-like object
|
|
47
|
-
|
|
48
|
-
```ts
|
|
49
|
-
const ref = { current: document.querySelector<HTMLFormElement>('#profile-form') }
|
|
50
|
-
const profile = form(ref)
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
The controller reads `target.current` only at construction time. If you mount the form later, recreate the controller or call `destroy()` and re-bind.
|
|
54
|
-
|
|
55
|
-
### By `null` / `undefined`
|
|
56
|
-
|
|
57
|
-
```ts
|
|
58
|
-
const profile = form()
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
Allowed for type-system ergonomics. The resulting controller has `element === null` and is effectively inert.
|
|
62
|
-
|
|
63
|
-
## Behaviour
|
|
64
|
-
|
|
65
|
-
On creation the controller:
|
|
66
|
-
|
|
67
|
-
1. Resolves the form element.
|
|
68
|
-
2. Merges the provided options with defaults (`autoValidate: true`, `clearErrorsOnSubmit: true`, `clearManualErrorsOnChange: true`, `attributes: { filled: 'css-filled', error: 'css-error' }`).
|
|
69
|
-
3. Wires delegated `input` and `submit` listeners, including controls associated through `form="id"`.
|
|
70
|
-
4. Starts a `MutationObserver` on the form subtree to track dynamic fields.
|
|
71
|
-
5. Optionally enables [`autoSubmit`](../api/auto-submit.md) when `options.autoSubmit` is truthy.
|
|
72
|
-
6. Synchronizes initial `css-filled` state for every tracked control, including when `autoValidate` is disabled.
|
|
73
|
-
7. Runs an initial validation pass only if `autoValidate` is enabled.
|
|
74
|
-
|
|
75
|
-
## Examples
|
|
76
|
-
|
|
77
|
-
### Minimal setup
|
|
78
|
-
|
|
79
|
-
```ts
|
|
80
|
-
import { form } from '@samline/forms'
|
|
81
|
-
|
|
82
|
-
const contact = form('contact-form')
|
|
83
|
-
|
|
84
|
-
contact.onSubmit(async (_element, _data, formData) => {
|
|
85
|
-
await fetch('/api/contact', { method: 'POST', body: formData })
|
|
86
|
-
})
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### With validators and options
|
|
90
|
-
|
|
91
|
-
```ts
|
|
92
|
-
import { form } from '@samline/forms'
|
|
93
|
-
|
|
94
|
-
const signup = form('signup-form', {
|
|
95
|
-
attributes: { filled: 'is-filled', error: 'is-invalid' },
|
|
96
|
-
autoValidate: true,
|
|
97
|
-
autoSubmit: { debounce: 600 },
|
|
98
|
-
clearManualErrorsOnChange: false,
|
|
99
|
-
validators: {
|
|
100
|
-
email: {
|
|
101
|
-
required: { value: true, message: 'Email is required.' },
|
|
102
|
-
pattern: {
|
|
103
|
-
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
104
|
-
message: 'Enter a valid email address.'
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
password: { required: true, minLength: 8 }
|
|
108
|
-
},
|
|
109
|
-
formats: {
|
|
110
|
-
phone: { type: 'phone', field: 'phone', options: { country: 'MX' } }
|
|
111
|
-
}
|
|
112
|
-
})
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### Bind later via ref
|
|
116
|
-
|
|
117
|
-
```ts
|
|
118
|
-
import { form } from '@samline/forms'
|
|
119
|
-
|
|
120
|
-
const profileRef = { current: null as HTMLFormElement | null }
|
|
121
|
-
|
|
122
|
-
const profile = form(profileRef)
|
|
123
|
-
|
|
124
|
-
document.querySelector('#mount')?.appendChild(document.createElement('form'))
|
|
125
|
-
profileRef.current = document.querySelector<HTMLFormElement>('#mount form')
|
|
126
|
-
|
|
127
|
-
// Re-bind because the controller captured `current` at construction time.
|
|
128
|
-
profile.destroy()
|
|
129
|
-
form(profileRef)
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
## Edge cases
|
|
133
|
-
|
|
134
|
-
- **The id is resolved once.** If the DOM element with that id is replaced, the controller keeps listening to the original element. Recreate the controller if you re-render the form.
|
|
135
|
-
- **`null` / `undefined` targets** are accepted and produce an inert controller. The chainable methods still return the controller, so they are safe to call as no-ops.
|
|
136
|
-
- **Reusing a target across multiple controllers** is supported but they each install their own listeners — call [`destroy()`](destroy.md) on the previous one to avoid duplicate work.
|
|
137
|
-
- **`autoValidate: false` disables the initial rule pass, not visual initialization.** Prefilled fields still receive `css-filled`; no validation errors are produced and `isValidated` remains `false`.
|
|
138
|
-
- **`createFormController` is also exported** as the lower-level factory; prefer `form` for ergonomic imports.
|
|
139
|
-
|
|
140
|
-
## `createFormController`
|
|
141
|
-
|
|
142
|
-
```ts
|
|
143
|
-
function createFormController(
|
|
144
|
-
target: FormTarget,
|
|
145
|
-
options?: FormControllerOptions
|
|
146
|
-
): FormController
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
This is the factory used by `form()` and accepts the same arguments. It is exported for integrations that prefer an explicit constructor-style name.
|
|
150
|
-
|
|
151
|
-
## Related
|
|
152
|
-
|
|
153
|
-
- [`FormController`](../typescript.md#formcontroller) — the returned controller type.
|
|
154
|
-
- [`destroy`](destroy.md) — tear the controller down.
|
|
155
|
-
- [`addCleanup`](add-cleanup.md) — attach integration cleanup to the controller lifecycle.
|
|
156
|
-
- [docs/options.md](../options.md) — every option in detail.
|