@reformer/core 11.0.0-beta.1 → 11.0.0-beta.10
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 +90 -68
- package/dist/behaviors-value-D05z3my6.js +109 -0
- package/dist/behaviors.d.ts +1 -1
- package/dist/behaviors.js +376 -211
- package/dist/create-model-DPSG3I0O.js +30 -0
- package/dist/date-utils-90-qZbse.js +43 -0
- package/dist/en-HP8XEych.js +44 -0
- package/dist/form/aggregate-signals.d.ts +21 -63
- package/dist/form/behaviors/collections.d.ts +84 -0
- package/dist/form/behaviors/context.d.ts +56 -0
- package/dist/form/behaviors/index.d.ts +30 -0
- package/dist/form/behaviors/internals.d.ts +41 -0
- package/dist/form/{behaviors-node.d.ts → behaviors/node.d.ts} +28 -5
- package/dist/form/behaviors/operators.d.ts +59 -0
- package/dist/form/behaviors/schema-operators.d.ts +83 -0
- package/dist/form/behaviors/types.d.ts +37 -0
- package/dist/form/create-form.d.ts +59 -70
- package/dist/form/flow/flow.d.ts +87 -0
- package/dist/form/form-bundle.d.ts +153 -0
- package/dist/form/form-level.d.ts +121 -0
- package/dist/form/form-proxy-builder.d.ts +6 -22
- package/dist/form/index.d.ts +23 -24
- package/dist/form/nodes/form-array-node.d.ts +100 -0
- package/dist/form/nodes/form-control-node.d.ts +73 -0
- package/dist/form/nodes/form-group-node.d.ts +66 -0
- package/dist/form/nodes/node-kind.d.ts +29 -0
- package/dist/form/nodes/state/availability.d.ts +6 -0
- package/dist/form/nodes/state/component.d.ts +6 -0
- package/dist/form/nodes/state/index.d.ts +12 -0
- package/dist/form/nodes/state/interaction.d.ts +13 -0
- package/dist/form/nodes/state/issues.d.ts +9 -0
- package/dist/form/nodes/state/pending.d.ts +6 -0
- package/dist/form/schema/compile.d.ts +59 -0
- package/dist/form/schema/compiled.d.ts +143 -0
- package/dist/form/schema/row-key.d.ts +11 -0
- package/dist/form/schema-controller.d.ts +159 -0
- package/dist/form/schema-subtree.d.ts +28 -0
- package/dist/form/signal-node-registry.d.ts +27 -22
- package/dist/form/submission.d.ts +93 -0
- package/dist/form/type-guards.d.ts +14 -73
- package/dist/form/types/contracts.d.ts +21 -85
- package/dist/form/types/field-config.d.ts +27 -0
- package/dist/form/types/index.d.ts +5 -57
- package/dist/form/types/nodes.d.ts +159 -0
- package/dist/form/types/schema-node.d.ts +147 -46
- package/dist/form/types/validation-schema.d.ts +25 -17
- package/dist/form/validation/context.d.ts +67 -0
- package/dist/form/validation/index.d.ts +24 -28
- package/dist/form/validation/operators.d.ts +101 -0
- package/dist/form/validation/run.d.ts +120 -0
- package/dist/form/validation/strategy.d.ts +89 -0
- package/dist/form/validation/types.d.ts +83 -0
- package/dist/form/validators/date-utils.d.ts +42 -0
- package/dist/form/validators/email.d.ts +23 -0
- package/dist/form/{validation/validators → validators}/file-type.d.ts +10 -10
- package/dist/form/{validation/validators → validators}/file-utils.d.ts +3 -2
- package/dist/form/validators/future-date.d.ts +23 -0
- package/dist/form/{validation/validators → validators}/index.d.ts +7 -4
- package/dist/form/validators/integer.d.ts +22 -0
- package/dist/form/validators/is-date.d.ts +24 -0
- package/dist/form/validators/is-number.d.ts +25 -0
- package/dist/form/validators/max-age.d.ts +28 -0
- package/dist/form/validators/max-date.d.ts +25 -0
- package/dist/form/{validation/validators → validators}/max-file-size.d.ts +10 -10
- package/dist/form/validators/max-files.d.ts +26 -0
- package/dist/form/validators/max-length.d.ts +33 -0
- package/dist/form/validators/max-total-file-size.d.ts +26 -0
- package/dist/form/validators/max.d.ts +26 -0
- package/dist/form/validators/min-age.d.ts +27 -0
- package/dist/form/validators/min-date.d.ts +25 -0
- package/dist/form/{validation/validators → validators}/min-file-size.d.ts +10 -10
- package/dist/form/validators/min-files.d.ts +27 -0
- package/dist/form/validators/min-length.d.ts +33 -0
- package/dist/form/validators/min.d.ts +26 -0
- package/dist/form/validators/multiple-of.d.ts +25 -0
- package/dist/form/validators/non-negative.d.ts +22 -0
- package/dist/form/validators/non-zero.d.ts +22 -0
- package/dist/form/validators/past-date.d.ts +23 -0
- package/dist/form/validators/pattern.d.ts +26 -0
- package/dist/form/{validation/validators → validators}/phone.d.ts +11 -11
- package/dist/form/validators/required.d.ts +29 -0
- package/dist/form/validators/url.d.ts +45 -0
- package/dist/form/validators/validation-issue.d.ts +8 -0
- package/dist/i18n/descriptor.d.ts +54 -0
- package/dist/i18n/en.json.d.ts +49 -0
- package/dist/i18n/loader.d.ts +71 -0
- package/dist/i18n/locale-store.d.ts +46 -0
- package/dist/i18n/locale.d.ts +90 -0
- package/dist/i18n/message-format.d.ts +136 -0
- package/dist/i18n/resolve-localized.d.ts +17 -0
- package/dist/i18n/ru.json.d.ts +49 -0
- package/dist/i18n/translator.d.ts +61 -0
- package/dist/i18n/validation-message.d.ts +53 -0
- package/dist/i18n.d.ts +26 -0
- package/dist/i18n.js +86 -0
- package/dist/index.d.ts +17 -5
- package/dist/index.js +942 -2601
- package/dist/loader-BWkigpVe.js +52 -0
- package/dist/locale/en.d.ts +2 -0
- package/dist/locale/en.js +5 -0
- package/dist/locale/en.json +45 -0
- package/dist/locale/index.d.ts +18 -0
- package/dist/locale/ru.d.ts +2 -0
- package/dist/locale/ru.js +44 -0
- package/dist/locale/ru.json +45 -0
- package/dist/locale-CG54AU2Q.js +219 -0
- package/dist/locale.d.ts +2 -0
- package/dist/locale.js +9 -0
- package/dist/{state → model}/behaviors-value.d.ts +24 -6
- package/dist/{state/form-model.d.ts → model/create-model.d.ts} +18 -4
- package/dist/{state → model}/derived-registry.d.ts +1 -1
- package/dist/model/index.d.ts +42 -0
- package/dist/model/model-nodes.d.ts +190 -0
- package/dist/model/model-signals-proxy.d.ts +63 -0
- package/dist/model/model-value-proxy.d.ts +84 -0
- package/dist/{state → model}/safe-effect.d.ts +50 -2
- package/dist/model/types.d.ts +239 -0
- package/dist/model.d.ts +2 -0
- package/dist/model.js +27 -0
- package/dist/node-kind-C-r6ZMdo.js +15 -0
- package/dist/platforms/react/field-binding/field-adapter.d.ts +87 -0
- package/dist/platforms/react/field-binding/field-handle.d.ts +53 -0
- package/dist/platforms/react/form-bundle-context.d.ts +27 -0
- package/dist/{form → platforms/react}/hooks/types.d.ts +58 -36
- package/dist/platforms/react/hooks/use-form-bundle.d.ts +41 -0
- package/dist/platforms/react/hooks/use-form-flow.d.ts +40 -0
- package/dist/{form → platforms/react}/hooks/use-form-validation.d.ts +13 -7
- package/dist/{form → platforms/react}/hooks/useArrayLength.d.ts +6 -6
- package/dist/{form → platforms/react}/hooks/useFormControl.d.ts +7 -9
- package/dist/{form → platforms/react}/hooks/useFormControlValue.d.ts +18 -18
- package/dist/{form → platforms/react}/hooks/useSignalSubscription.d.ts +9 -1
- package/dist/platforms/react/i18n/context.d.ts +18 -0
- package/dist/platforms/react/i18n/hooks.d.ts +65 -0
- package/dist/platforms/react/i18n/provider.d.ts +59 -0
- package/dist/platforms/react/index.d.ts +30 -0
- package/dist/react.d.ts +22 -0
- package/dist/react.js +505 -0
- package/dist/runtime-token.d.ts +1 -2
- package/dist/safe-effect-BazLOv1g.js +669 -0
- package/dist/schema-controller-CpiO2JoK.js +143 -0
- package/dist/signals.d.ts +15 -9
- package/dist/signals.js +11 -6
- package/dist/strategy-DzvnsWLh.js +375 -0
- package/dist/type-guards-D-N_5Ddh.js +19 -0
- package/dist/validation-issue-B9mjI8jz.js +7 -0
- package/dist/validation-message-Ced9MGxs.js +194 -0
- package/dist/validation.d.ts +1 -1
- package/dist/validation.js +11 -165
- package/dist/validators/email.d.ts +1 -1
- package/dist/validators/email.js +5 -8
- package/dist/validators/future-date.d.ts +1 -1
- package/dist/validators/future-date.js +10 -13
- package/dist/validators/integer.d.ts +1 -1
- package/dist/validators/integer.js +4 -7
- package/dist/validators/is-date.d.ts +1 -1
- package/dist/validators/is-date.js +5 -8
- package/dist/validators/is-number.d.ts +1 -1
- package/dist/validators/is-number.js +4 -7
- package/dist/validators/max-age.d.ts +1 -1
- package/dist/validators/max-age.js +8 -12
- package/dist/validators/max-date.d.ts +1 -1
- package/dist/validators/max-date.js +10 -13
- package/dist/validators/max-length.d.ts +1 -1
- package/dist/validators/max-length.js +7 -10
- package/dist/validators/max.d.ts +1 -1
- package/dist/validators/max.js +4 -7
- package/dist/validators/min-age.d.ts +1 -1
- package/dist/validators/min-age.js +10 -11
- package/dist/validators/min-date.d.ts +1 -1
- package/dist/validators/min-date.js +8 -11
- package/dist/validators/min-length.d.ts +1 -1
- package/dist/validators/min-length.js +4 -7
- package/dist/validators/min.d.ts +1 -1
- package/dist/validators/min.js +4 -7
- package/dist/validators/multiple-of.d.ts +1 -1
- package/dist/validators/multiple-of.js +11 -10
- package/dist/validators/non-negative.d.ts +1 -1
- package/dist/validators/non-negative.js +2 -5
- package/dist/validators/non-zero.d.ts +1 -1
- package/dist/validators/non-zero.js +4 -7
- package/dist/validators/past-date.d.ts +1 -1
- package/dist/validators/past-date.js +10 -13
- package/dist/validators/pattern.d.ts +1 -1
- package/dist/validators/pattern.js +5 -8
- package/dist/validators/phone.d.ts +1 -1
- package/dist/validators/phone.js +10 -11
- package/dist/validators/required.d.ts +1 -1
- package/dist/validators/required.js +4 -7
- package/dist/validators/url.d.ts +1 -1
- package/dist/validators/url.js +48 -17
- package/dist/validators.d.ts +1 -1
- package/dist/validators.js +86 -108
- package/docs/llms/01-api-reference.md +60 -34
- package/docs/llms/02-quick-start.md +112 -74
- package/docs/llms/03-api-signatures.md +369 -148
- package/docs/llms/04-common-patterns.md +58 -56
- package/docs/llms/05-common-mistakes.md +69 -44
- package/docs/llms/06-troubleshooting.md +14 -9
- package/docs/llms/07-complete-import.md +74 -43
- package/docs/llms/08-form-types.md +11 -4
- package/docs/llms/09-formschema.md +132 -84
- package/docs/llms/10-arrays.md +141 -61
- package/docs/llms/11-async-watchfield.md +2 -2
- package/docs/llms/13-multi-step.md +134 -63
- package/docs/llms/14-extended-mistakes.md +20 -17
- package/docs/llms/15-project-structure.md +224 -70
- package/docs/llms/16-ui-components.md +86 -46
- package/docs/llms/17-nonexistent-api.md +217 -32
- package/docs/llms/18-conditional-fields.md +71 -32
- package/docs/llms/19-reading-values.md +45 -4
- package/docs/llms/20-compute-vs-watch.md +25 -10
- package/docs/llms/21-array-operations.md +76 -26
- package/docs/llms/22-cycle-detection.md +1 -1
- package/docs/llms/23-copy-from.md +2 -2
- package/docs/llms/24-sync-fields.md +6 -5
- package/docs/llms/25-reset-when.md +14 -7
- package/docs/llms/26-transform-value.md +4 -4
- package/docs/llms/27-revalidate-when.md +16 -15
- package/docs/llms/28-submit-and-reset.md +278 -128
- package/docs/llms/29-async-preload.md +41 -31
- package/docs/llms/30-type-safety-recipes.md +105 -51
- package/docs/llms/31-async-validator-debounce.md +96 -53
- package/docs/llms/33-validation-strategy.md +178 -66
- package/docs/llms/34-i18n.md +298 -0
- package/docs/llms/35-unified-contract.md +309 -0
- package/docs/llms/36-issue-messages.md +284 -0
- package/docs/llms/37-compiled-schema.md +332 -0
- package/docs/llms/38-form-flow.md +396 -0
- package/llms-index.json +1 -0
- package/llms.txt +10690 -3089
- package/package.json +48 -9
- package/dist/behaviors-node-DGEFMTQu.js +0 -18
- package/dist/behaviors-value-C7dSEhCc.js +0 -134
- package/dist/date-utils-dTT_x_Be.js +0 -31
- package/dist/derived-registry-CluZCQ7U.js +0 -16
- package/dist/form/behaviors.d.ts +0 -133
- package/dist/form/error-handler.d.ts +0 -166
- package/dist/form/factories/index.d.ts +0 -6
- package/dist/form/factories/node-factory.d.ts +0 -188
- package/dist/form/form-submitter.d.ts +0 -121
- package/dist/form/nodes/array-node.d.ts +0 -276
- package/dist/form/nodes/field-node.d.ts +0 -294
- package/dist/form/nodes/form-node.d.ts +0 -326
- package/dist/form/nodes/group-node.d.ts +0 -284
- package/dist/form/nodes/model-array-node.d.ts +0 -121
- package/dist/form/status-machine.d.ts +0 -115
- package/dist/form/types/deep-schema.d.ts +0 -107
- package/dist/form/types/form-proxy.d.ts +0 -141
- package/dist/form/unique-id.d.ts +0 -45
- package/dist/form/validation/validators/date-utils.d.ts +0 -30
- package/dist/form/validation/validators/email.d.ts +0 -23
- package/dist/form/validation/validators/future-date.d.ts +0 -23
- package/dist/form/validation/validators/integer.d.ts +0 -22
- package/dist/form/validation/validators/is-date.d.ts +0 -23
- package/dist/form/validation/validators/is-number.d.ts +0 -24
- package/dist/form/validation/validators/max-age.d.ts +0 -25
- package/dist/form/validation/validators/max-date.d.ts +0 -25
- package/dist/form/validation/validators/max-files.d.ts +0 -26
- package/dist/form/validation/validators/max-length.d.ts +0 -27
- package/dist/form/validation/validators/max-total-file-size.d.ts +0 -26
- package/dist/form/validation/validators/max.d.ts +0 -25
- package/dist/form/validation/validators/min-age.d.ts +0 -25
- package/dist/form/validation/validators/min-date.d.ts +0 -25
- package/dist/form/validation/validators/min-files.d.ts +0 -26
- package/dist/form/validation/validators/min-length.d.ts +0 -27
- package/dist/form/validation/validators/min.d.ts +0 -25
- package/dist/form/validation/validators/multiple-of.d.ts +0 -24
- package/dist/form/validation/validators/non-negative.d.ts +0 -22
- package/dist/form/validation/validators/non-zero.d.ts +0 -22
- package/dist/form/validation/validators/past-date.d.ts +0 -23
- package/dist/form/validation/validators/pattern.d.ts +0 -33
- package/dist/form/validation/validators/required.d.ts +0 -32
- package/dist/form/validation/validators/url.d.ts +0 -43
- package/dist/form/validation-schema.d.ts +0 -106
- package/dist/form/validation-strategy.d.ts +0 -55
- package/dist/form/validators.d.ts +0 -11
- package/dist/form-model-DYCzGGKF.js +0 -286
- package/dist/state/index.d.ts +0 -31
- package/dist/state/subscription-manager.d.ts +0 -180
- package/dist/state/types.d.ts +0 -144
- package/dist/state.d.ts +0 -2
- package/dist/state.js +0 -21
- package/dist/subscription-manager-705bls97.js +0 -171
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
## 15. NON-EXISTENT API (DO NOT USE)
|
|
2
2
|
|
|
3
|
-
**Следующего API НЕТ в @reformer/core** (наследие старой path-based
|
|
4
|
-
|
|
3
|
+
**Следующего API НЕТ в @reformer/core** (наследие старой path-based архитектуры и прежних
|
|
4
|
+
контрактов модели, валидации, схемы, визарда и нод):
|
|
5
5
|
|
|
6
|
-
> ⚠️ Операторы валидации `validate`/`validateAsync`/`validateWhen`/`
|
|
7
|
-
> **существуют** — но живут в сабпути `@reformer/core/validation` (
|
|
8
|
-
> `
|
|
6
|
+
> ⚠️ Операторы валидации `validate`/`validateAsync`/`validateWhen`/`apply`/`applyEach`
|
|
7
|
+
> **существуют** — но живут в сабпути `@reformer/core/validation` (схема
|
|
8
|
+
> `defineValidation(({ model, cross }) => …)`, раннер `validateModel`), а НЕ в корне и НЕ
|
|
9
9
|
> в `@reformer/core/validators`. Ниже перечислено то, чего действительно нет.
|
|
10
10
|
|
|
11
11
|
| Wrong | Correct | Notes |
|
|
12
12
|
| -------------------------------- | ------------------------------------------------ | ---------------------------------------------- |
|
|
13
|
-
| `useForm` | `
|
|
13
|
+
| `useForm` | `createForm` + `useFormBundle` | Хука useForm нет |
|
|
14
|
+
| `FormValue` | тип значения самого поля либо `unknown` | Тип удалён: модель обобщённая (`createModel<T>`), параметры замечания — `Record<string, unknown>` |
|
|
15
|
+
| `value instanceof Signal` | `isSignal(value)` из `@reformer/core/signals` | Проверка по классу не узнаёт сигнал другой копии рантайма; ручку поля проверяет `isValueSignal` |
|
|
16
|
+
| `import { useFormControl, useFormBundle, … } from '@reformer/core'` | те же имена из `@reformer/core/react` | Корень ядра без React: хуки (`useFormControl`, `useFormControlValue`, `useArrayLength`, `useFormValidation`, `useFormBundle`, `useFormFlow`), `FormBundleContext`, связка поля (`bindFieldProps`, `getFieldAdapter`, `FieldAdapter`, `FieldHandle`) и типы снимков (`FieldControlState`, `ArrayControlState`) отдаёт только сабпат `/react` |
|
|
17
|
+
| `import { I18nProvider, useI18n, … } from '@reformer/core/i18n'` | те же имена из `@reformer/core/react` | `/i18n` — локаль, перевод и тексты замечаний без React; провайдер и хуки (`I18nProvider`, `useI18n`, `useMessages`, `useIssueMessage`) — в `/react` |
|
|
18
|
+
| `ElementType` / `ComponentType` в типе узла схемы | `ComponentRef` из `@reformer/core` | Типы схемы и ноды не импортируют типы React: `component` — непрозрачная ссылка (`string | object`), к типу своего UI-слоя её приводит рендерер |
|
|
14
19
|
| `validateForm`, `validateFormModel` | `validateModel(model, schema)` из `@reformer/core/validation` | Legacy-движок дерева `{ value, validators }` удалён; внешний раннер — `validateModel` |
|
|
15
20
|
| `validateModelSync` | `await validateModel(model, schema)` | Синхронного раннера нет — прогон асинхронный (`Promise<boolean>`) |
|
|
16
21
|
| `applyWhen` | `validateWhen(() => cond, () => { … })` | Условная валидация — оператором `validateWhen`, не узлом `{ when, children }` |
|
|
17
|
-
| `validateItems`, `validateGroup`, `validateTree` | `
|
|
18
|
-
| `ValidationSchemaFn`, `BehaviorSchemaFn` | `
|
|
19
|
-
| `equalTo`, `custom`, `notEmpty` (validators) | inline `Rule<T>` `(value) =>
|
|
20
|
-
| `form.submit()` / `form.validate()` прогоняют схему | `await validateModel(model, schema)` перед submit/шагом | submit/validate НЕ запускают ambient-схему валидации |
|
|
22
|
+
| `validateItems`, `validateGroup`, `validateTree` | `applyEach(model.$.arr, rowRules)` / `apply(model.$.group, groupRules)` | Правила строк и подформы — отдельные схемы, подключённые привязкой |
|
|
23
|
+
| `ValidationSchemaFn`, `BehaviorSchemaFn` | `ValidationDefinition<T>` (`defineValidation`) / `defineFormBehavior` | Типы path-схем удалены |
|
|
24
|
+
| `equalTo`, `custom`, `notEmpty` (validators) | inline `Rule<T>` `(value) => issue \| null` в `validate(sig, [...])`; сравнение полей — `cross(sig, fn)` | Таких фабрик нет; кастомное правило — обычная функция значения, cross-field — `cross` |
|
|
21
25
|
| `FieldPath`, `FieldPathNode` | `model.$.field` (`PathAwareSignal`) | Пути заменены сигналами |
|
|
22
26
|
| `ctx.form.x.value.value` | `model.x` / `model.$.x.value` | В behaviors читаем модель напрямую |
|
|
23
27
|
| `ctx.setFieldValue(name, value)` | `model.x = value` / `compute(...)` | Не существует |
|
|
@@ -26,51 +30,232 @@
|
|
|
26
30
|
| `FormProvider`, `control` prop, `register()` | `<Component form={form} />`, `useFormControl(form.field)` | Форма передаётся через props |
|
|
27
31
|
| `getFieldValue()` | `model.field` / `useFormControlValue(form.field)`| Не существует |
|
|
28
32
|
|
|
33
|
+
### Удалено: модель (этап 1)
|
|
34
|
+
|
|
35
|
+
| Wrong | Correct | Notes |
|
|
36
|
+
| -------------------------------------- | ---------------------------------------------------- | --------------------------------------- |
|
|
37
|
+
| `model.set(partial)` для части полей | `model.patch(partial)` | `set` — полная установка: недостающие ключи в dev дают предупреждение и не меняются; `patch` сливает переданные ключи, вложенные группы — тоже частично, массив — целиком |
|
|
38
|
+
| `FormValue`, `T extends FormValue` | `T` без ограничения / `Record<string, unknown>` | Тип удалён |
|
|
39
|
+
|
|
40
|
+
### Удалено: компонент массива (этап 2)
|
|
41
|
+
|
|
42
|
+
Имя `FormArray` теперь занято **типом ноды** массива под-форм в `@reformer/core`; компонент массива
|
|
43
|
+
в cdk и ui-kit называется `FieldArray`.
|
|
44
|
+
|
|
45
|
+
| Wrong | Correct | Notes |
|
|
46
|
+
| ------------------------------------------------- | -------------------------------------------------- | -------------------------------------- |
|
|
47
|
+
| компонент `FormArray` (`@reformer/cdk`, `@reformer/ui-kit`) | `FieldArray` | `{ model: model.$.items, component: FieldArray, item }` |
|
|
48
|
+
| `FormArraySection` | `FieldArraySection` | ui-kit |
|
|
49
|
+
| `useFormArray` | `useFieldArray` | cdk |
|
|
50
|
+
| `@reformer/cdk/form-array`, `@reformer/ui-kit/form-array` | `@reformer/cdk/field-array`, `@reformer/ui-kit/field-array` | сабпаты |
|
|
51
|
+
| ключи локали `kit.formArray.*` | `kit.fieldArray.*` | ui-kit |
|
|
52
|
+
|
|
53
|
+
JSON-документ схемы со старыми именами `FormArray` / `FormArraySection` реестр renderer-json ещё
|
|
54
|
+
принимает как псевдонимы (dev-предупреждение).
|
|
55
|
+
|
|
56
|
+
### Удалено: замечание валидации без текста (этап 4)
|
|
57
|
+
|
|
58
|
+
Замечание несёт код и параметры; текст по коду даёт словарь формы — см. `36-issue-messages.md`.
|
|
59
|
+
|
|
60
|
+
| Wrong | Correct | Notes |
|
|
61
|
+
| ------------------------------------------------- | -------------------------------------------------- | -------------------------------------- |
|
|
62
|
+
| `required({ message: '…' })`, `{ messageKey }` | `required({ code: 'phone.required' })` + `defineIssueMessages({ 'phone.required': '…' })` | У `RuleOptions` только `code` и `params` |
|
|
63
|
+
| `ValidationError` (`{ code, message, … }`) | `ValidationIssue` (`{ code, params?, severity? }`) | Поля `message` нет — текст через резолвер |
|
|
64
|
+
| `ValidateOptions` | `RuleOptions` | `{ code?, params? }` |
|
|
65
|
+
| `ValidationSchema`, `defineValidationSchema` | `ValidationDefinition`, `defineValidation` | `@reformer/core/validation` |
|
|
66
|
+
| `resolveValidationError` | `resolveIssueMessage(issue, i18n, messages?)` | `@reformer/core/i18n` |
|
|
67
|
+
| `useValidationMessage` | `useIssueMessage(messages?)` | `@reformer/core/react` |
|
|
68
|
+
| `node.errors`, `setErrors`, `clearErrors` | `node.issues`, `setIssues`, `clearIssues` | И в снимке `useFormControl` — `issues` |
|
|
69
|
+
| `result.errors` у `ValidationResult` | `result.issues` | Замечания по ручке поля |
|
|
70
|
+
| `getErrors(filter)`, `ErrorFilterOptions` | `node.issues.value.filter(…)` | Фильтр — обычный `filter` |
|
|
71
|
+
| `errors[0].message` в разметке | `useIssueMessage(messages)(issues[0])` | Строку даёт резолвер при показе |
|
|
72
|
+
| cdk `createMessageResolver`, `defaultErrorResolver`, `ValidationMessageTable` | `<ValidationMessagesProvider messages={defineIssueMessages({…})}>` | Словарь формы поверх локали |
|
|
73
|
+
| cdk `ValidationErrorResolver`, `useValidationErrorResolver` | `IssueMessageResolver`, `useIssueMessageResolver` | `@reformer/cdk` |
|
|
74
|
+
|
|
75
|
+
### Удалено: схема (этап 5)
|
|
76
|
+
|
|
77
|
+
| Wrong | Correct | Notes |
|
|
78
|
+
| ------------------------------------------------- | -------------------------------------------------- | -------------------------------------- |
|
|
79
|
+
| `bundle.render.tree` как авторское дерево | `bundle.render.tree` — `CompiledSchema`; авторское дерево — `bundle.render.tree.root.source` | См. `37-compiled-schema.md` |
|
|
80
|
+
| повтор `selector` в одной области «с предупреждением» | разные селекторы | Повтор — исключение при сборке |
|
|
81
|
+
| свой разбор ключей `model` / `item` / `part` у узла | `compileSchema(tree, model)` — `kind` узла | `field \| array \| part \| container \| text` |
|
|
82
|
+
|
|
83
|
+
### Удалено: уровень формы, поток шагов, визард (этап 6)
|
|
84
|
+
|
|
85
|
+
| Wrong | Correct | Notes |
|
|
86
|
+
| --------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
87
|
+
| `bundle.validation.validateAll()` / `.runAll()` | `bundle.validate()` → `ValidationResult` | Поле `bundle.validation` осталось служебным (`{ rules, controller }` для `useFormBundle`) — методов проверки на нём нет |
|
|
88
|
+
| `bundle.validation.validateStep(n)` / `.runStep(n)` | `bundle.validateStep(id)` → `ValidationResult` | Идентификатор шага — `selector`, не номер |
|
|
89
|
+
| `createForm({ validation: { steps: { loan: rules } } })` | `createForm({ flow: { steps: [{ id: 'loan', rules }] } })` | Правила вне шагов — `validation: { extras }` |
|
|
90
|
+
| `buildValidation`, `FormValidationBundle` | `createForm({ validation, flow })` / `createFormLevel` | Уровень формы собирает сборка |
|
|
91
|
+
| `form.submit(handler)`, `SubmitOptions`, `form.submitting` | `bundle.submission.submit(handler)` → `SubmitResult`, `bundle.submission.submitting` | Один прогон правил; обработчик — только при `valid` |
|
|
92
|
+
| `bundle.submit(handler)`, `bundle.submitting` | `bundle.submission.submit(handler)` → `SubmitResult`, `bundle.submission.submitting` | Отправка — отдельный объект сборки; псевдонимов нет. То же у `level` в `renderBehavior` и бандлов `createReactForm` / `createJsonForm`: `level.submission.submit(handler)` |
|
|
93
|
+
| `form.validate()` | `bundle.validate()` | У нод методов проверки нет |
|
|
94
|
+
| `<FormWizard config={…}>`, `FormWizardConfig` | `<FormWizard flow={bundle.flow}>` | Без `flow` и сборки в контексте — позиционный поток без правил |
|
|
95
|
+
| `<FormWizard ref={…}>`, `FormWizardHandle`, `ref.current.goToStep(n)` | `bundle.flow.goTo(id)` / `next()` / `previous()` | Навигация — методами потока |
|
|
96
|
+
| `defineSteps(model, { steps, extras, strategy })` | `createForm({ validation: { extras, strategy }, flow })` | Живая проверка — стратегия формы |
|
|
97
|
+
| `useWizardStepValidation(config)` | `validation: { strategy }` сборки | Стратегию армирует `useFormBundle` |
|
|
98
|
+
| `makeValidationConfig(model)` (`validateStep(n)` / `validateAll()`) | `flow` сборки | Визард берёт поток, а не конфиг |
|
|
99
|
+
|
|
100
|
+
### Удалено: ноды формы — контракты вместо классов (этап 7)
|
|
101
|
+
|
|
102
|
+
| Wrong | Correct | Notes |
|
|
103
|
+
| ------------------------------------------------- | -------------------------------------------------- | -------------------------------------- |
|
|
104
|
+
| классы `FormNode`, `FieldNode`, `GroupNode`, `ModelArrayNode` | интерфейсы `FormNode`, `FormControl`, `FormGroup`, `FormArray` | Классы реализации внутренние; ноды строит сборка |
|
|
105
|
+
| `FormProxy<T>` | `FormGroup<T>` | Тип группы |
|
|
106
|
+
| `isFieldNode`, `isGroupNode`, `isArrayNode` | `isFormControl`, `isFormGroup`, `isFormArray` | Гарды по метке вида; `isFormNode` остался |
|
|
107
|
+
| `node instanceof FieldNode` | `isFormControl(node)` | `instanceof` по интерфейсу невозможен |
|
|
108
|
+
| `node.status.value`, `FieldStatus` | `node.valid` / `invalid` / `pending` / `disabled` | Отдельного статуса нет |
|
|
109
|
+
| `node.patchValue(v)` | `node.setValue(v)` / `model.patch(partial)` | Частичное обновление — у модели |
|
|
110
|
+
| `markAsDirty()`, `markAsPristine()` | `dirty` ставит `setValue(v)`, снимает `reset()` | Изменились ли значения модели — `model.isDirty()` |
|
|
111
|
+
| `node.validate()` | `bundle.validate()` / `bundle.validateStep(id)` | |
|
|
112
|
+
| `group.fields` (Map), `group.getProxy()` | `form.<поле>`, `form.$` (все поля), `Object.values(form.$)` | `fields` теперь обычное поле данных |
|
|
113
|
+
| `node.id` | ключ строки — `array.keyAt(index)` | `id` теперь обычное поле данных |
|
|
114
|
+
| `array.items` | `array.at(i)` / `map` / `forEach` | |
|
|
115
|
+
| `array.length.value` | `array.length` (число; в эффекте подписывает) | Не сигнал |
|
|
116
|
+
| `key={row.id}` / `key={index}` у строк массива | `key={array.keyAt(index)}` | Ключ не меняется при `move` / `swap` |
|
|
117
|
+
|
|
118
|
+
Затеняют поле группы только члены `FormNode` (`value`, `issues`, `reset`, …): такое поле берут через
|
|
119
|
+
`form.$.value`. Имена `id`, `status`, `fields`, `submit` — обычные поля данных.
|
|
120
|
+
|
|
121
|
+
### Удалено вместе со старым путём сборки формы
|
|
122
|
+
|
|
123
|
+
Форма собирается только из модели: `createForm({ initial | model, schema })`. Ноды строятся по
|
|
124
|
+
виду узла модели, правила живут в схеме валидации.
|
|
125
|
+
|
|
126
|
+
| Wrong | Correct | Notes |
|
|
127
|
+
| -------------------------------- | ------------------------------------------------ | ---------------------------------------------- |
|
|
128
|
+
| `createLegacyForm(schema)` | `createForm({ initial, schema })` | Формы без модели нет |
|
|
129
|
+
| `new FormGroup({ email: { value, component } })` | `createFormFromModel({ model, schema })` | `FormGroup` — тип, ноды строит сборка |
|
|
130
|
+
| `new FormControl({ value: '' })` | `createForm({ initial: { email: '' } }).form.email` | `FormControl` — тип; значение принадлежит модели |
|
|
131
|
+
| `FieldConfig.validators` / `asyncValidators` | `validate` / `validateAsync` в `defineValidation` | Собственных валидаторов у ноды нет |
|
|
132
|
+
| `updateOn`, `debounce` узла поля; `field.setUpdateOn()` / `getUpdateOn()` | `validation: { strategy, debounce }` в `createForm` | Когда запускать проверку, решает стратегия валидации формы |
|
|
133
|
+
| `field.setValue(value, { emitEvent: false })` | `field.setValue(value)` | Опций у `setValue` нет |
|
|
134
|
+
| `ArrayNode` | `FormArray` | Нода массива одна — над массивом модели |
|
|
135
|
+
| `NodeFactory` | — | Сборка по ключам конфига удалена |
|
|
136
|
+
| `FormErrorHandler`, `ErrorStrategy` | — | Обслуживали валидаторы ноды |
|
|
137
|
+
| `FormSchema<T>`, `GroupNodeConfig<T>` | `FormSchemaNode` | Схема — дерево узлов, а не запись по полям |
|
|
138
|
+
| `ValidatorFn`, `AsyncValidatorFn`, `Validator<TForm, TField>` | `Rule<T>`, `AsyncRule<T>` из `@reformer/core/validation` | Правило — функция одного аргумента |
|
|
139
|
+
|
|
140
|
+
### Удалено: подписки на нодах, доступ по строковому пути, псевдонимы
|
|
141
|
+
|
|
142
|
+
Реакции живут на слое модели (`defineFormBehavior`), поле берётся через форму, у каждой
|
|
143
|
+
операции одно имя.
|
|
144
|
+
|
|
145
|
+
| Wrong | Correct | Notes |
|
|
146
|
+
| ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
|
|
147
|
+
| `field.watch(cb)`, `form.watchField('email', cb)`, `form.watchFieldByPath(path, cb)` | `onChange(model.$.email, cb)` в `defineFormBehavior`; вне поведения — `watchField(model.$.email, cb)` из `@reformer/core/model` | Подписок на ноде нет |
|
|
148
|
+
| `field.computeFrom(sources, fn)`, `form.linkFields('a', 'b', fn)` | `compute(model.$.b, () => fn(model.a))` в `defineFormBehavior` | Вычисляемое значение — оператор поведения |
|
|
149
|
+
| `field.dispose()` | сборку — `bundle.dispose()`; форму из `createFormFromModel` — `disposeForm(form)` | В контракте нод `dispose` нет |
|
|
150
|
+
| `form.getFieldByPath('address.city')`, `form.getField('email')` | `form.address.city`, `form.email`; имя, занятое членом ноды, — `form.$.value` | Строковых путей к нодам нет |
|
|
151
|
+
| `form.getAllFields()` | `Object.values(form.$)` | `form.$` — ноды всех полей группы по имени |
|
|
152
|
+
| `form.touchAll()` | `form.markAsTouched()` | Отметка каскадная: уходит во все вложенные поля |
|
|
153
|
+
| `form.resetToInitial()`, `field.resetToInitial()` | `form.reset()` — к значениям создания формы; `model.reset()` — к точке отсчёта модели | См. `28-submit-and-reset.md` |
|
|
154
|
+
| `form.submitWithResult(handler)` | `bundle.submission.submit(handler)` → `SubmitResult` | Проверка и отправка одним вызовом |
|
|
155
|
+
| `getNodeType(node)` | `isFormControl` / `isFormGroup` / `isFormArray` | Вид ноды — гардами |
|
|
156
|
+
| `each(model.items, (item) => …)` | `applyEach(model.$.items, itemRules)` | Правила строки — отдельная схема над элементом |
|
|
157
|
+
| `import { cross } from '@reformer/core/validation'` | `defineValidation(({ model, cross }) => …)` | `cross` — только из аргумента схемы |
|
|
158
|
+
| `createCoreForm`, `CreateCoreFormConfig` | `createForm`, `CreateFormConfig` | Сборка одна |
|
|
159
|
+
| `createReactForm`, `useReactForm` (`@reformer/renderer-react`); `createJsonForm`, `useJsonForm` (`@reformer/renderer-json`) | `createForm` + `useFormBundle`; документ JSON — `createForm({ schema: document, registry })` | Фабрики слоёв удалены — сборка одна |
|
|
160
|
+
| `JsonFormRenderer`, `JsonRendererProvider` | `<FormRenderer form={bundle} />` из `@reformer/renderer-react` | Реестр уходит в сборку, а не в контекст |
|
|
161
|
+
| `renderBehavior`, `RenderBehaviorFn` | одно поведение `defineFormBehavior(({ model, form, schema, flow }) => …)` | Правила узлов — в поведении формы |
|
|
162
|
+
| `CoreForm`, `CreateFormConfigBase` | `FormBundle`, `CreateFormConfig` | Общие типы удалённых фабрик |
|
|
163
|
+
| `FormArrayProxy<T>` | `FormArray<T>` | Псевдоним типа удалён |
|
|
164
|
+
| `FormSubmitter`, `SubmittableForm`, `SubscriptionManager`, `createSchemaOverrideMaps`, `ModelArrayControl`, `FormControlsProxy` | — | Внутренние детали, из пакета не экспортируются |
|
|
165
|
+
|
|
166
|
+
### Прежние ключи узла схемы
|
|
167
|
+
|
|
168
|
+
| Wrong | Correct | Notes |
|
|
169
|
+
| -------------------------------- | ------------------------------------------------ | ---------------------------------------------- |
|
|
170
|
+
| `{ value: model.$.email, component }` | `{ model: model.$.email, component }` | Привязка поля — ключ `model` |
|
|
171
|
+
| `{ array: model.items, item }` | `{ model: model.$.items, item }` | Привязка массива под-форм — ручка, а не фасад |
|
|
172
|
+
| `{ email: { model, component } }` (запись «имя → узел») | `{ children: [{ model, component }] }` | Вложенные узлы читаются только из `children` |
|
|
173
|
+
| узел внутри `componentProps` | узел в `children` | В пропсы обход схемы не заглядывает |
|
|
174
|
+
| `testId` ключом узла | `componentProps: { testId }` | Ключа узла `testId` нет |
|
|
175
|
+
|
|
176
|
+
### Операторы над сигналами — не в корне
|
|
177
|
+
|
|
178
|
+
`computeFrom`, `copyFrom`, `watchField`, `transformValue`, `resetWhen`, `syncFields`,
|
|
179
|
+
`revalidateWhen`, `enableWhen`, `disableWhen` из `@reformer/core` не импортируются.
|
|
180
|
+
|
|
181
|
+
| Wrong | Correct |
|
|
182
|
+
| -------------------------------- | ------------------------------------------------ |
|
|
183
|
+
| `import { enableWhen, copyFrom } from '@reformer/core'` | `import { enableWhen, copyFrom } from '@reformer/core/behaviors'` — внутри `defineFormBehavior` |
|
|
184
|
+
| `import { watchField } from '@reformer/core'` | `onChange` из `@reformer/core/behaviors`; сам примитив — `@reformer/core/model` |
|
|
185
|
+
| `import { computeFrom } from '@reformer/core'` | `compute` / `computeFrom` из `@reformer/core/behaviors` |
|
|
186
|
+
|
|
187
|
+
### UI: удалённые field-версии компонентов (`@reformer/ui-kit`)
|
|
188
|
+
|
|
189
|
+
Отдельных «field-версий» у компонентов кита больше нет: в `component` поля кладётся сам
|
|
190
|
+
компонент, диалект он объявляет статикой `reformerAdapter`, связывает поле обёртка
|
|
191
|
+
(`FormField.Control` / рендерер).
|
|
192
|
+
|
|
193
|
+
| Wrong | Correct |
|
|
194
|
+
| ---------------------------------------------- | --------------------------------------------------------- |
|
|
195
|
+
| `InputField`, `SelectField`, `CheckboxField`, … (любой `*Field`) | `Input`, `SelectAsync`, `CheckboxWithLabel`, … — таблица в ui-kit `01-overview.md` |
|
|
196
|
+
| `InputField` + `type: 'number'` | `InputNumber` |
|
|
197
|
+
| `InputField` + `suggestions` | `InputSuggest` |
|
|
198
|
+
| `FileUploadField` + `variant: 'dropzone' \| 'input'` | `FileUploadDropzone` / `FileUploadInput` (без `variant`) |
|
|
199
|
+
| `withFormControl(MyControl, adapter)` | `defineFieldControl(MyControl, { adapter })` из `@reformer/ui-kit/fields` |
|
|
200
|
+
|
|
29
201
|
### Common Import Errors
|
|
30
202
|
|
|
31
203
|
```typescript
|
|
32
204
|
// WRONG - этих символов / путей НЕ существует
|
|
33
205
|
import { useForm, validateForm, validateFormModel } from '@reformer/core'; // NO!
|
|
206
|
+
import { useFormControl, useFormBundle } from '@reformer/core'; // NO! хуки — из /react
|
|
34
207
|
import { validate, applyWhen, equalTo } from '@reformer/core/validators'; // NO! операторов тут нет
|
|
208
|
+
import { defineValidationSchema, type ValidationSchema } from '@reformer/core/validation'; // NO!
|
|
209
|
+
import { resolveValidationError, useValidationMessage } from '@reformer/core/i18n'; // NO!
|
|
210
|
+
import { FieldNode, GroupNode, isFieldNode, type FormProxy } from '@reformer/core'; // NO!
|
|
211
|
+
import { defineSteps, useWizardStepValidation } from '@reformer/cdk/form-wizard'; // NO!
|
|
212
|
+
import { FormArray, useFormArray } from '@reformer/cdk/form-array'; // NO! — field-array
|
|
35
213
|
import { transformers } from '@reformer/core/behaviors'; // NO!
|
|
36
|
-
import type { FieldPath, ValidationSchemaFn } from '@reformer/core';
|
|
214
|
+
import type { FieldPath, ValidationSchemaFn, ValidationError } from '@reformer/core'; // NO!
|
|
37
215
|
|
|
38
216
|
// CORRECT
|
|
39
|
-
import { createModel, createForm,
|
|
40
|
-
import
|
|
41
|
-
|
|
217
|
+
import { createModel, createForm, isFormControl } from '@reformer/core';
|
|
218
|
+
import { useFormBundle, useFormControl, useIssueMessage } from '@reformer/core/react';
|
|
219
|
+
import type { FormModel, FormGroup, FormSchemaNode, ValidationIssue } from '@reformer/core';
|
|
220
|
+
// Операторы валидации + раннер + defineValidation — отдельный сабпуть:
|
|
42
221
|
import {
|
|
43
|
-
validate, validateAsync, validateWhen,
|
|
44
|
-
|
|
45
|
-
type Rule, type AsyncRule, type
|
|
222
|
+
validate, validateAsync, validateWhen, apply, applyEach,
|
|
223
|
+
defineValidation, validateModel, runValidation, applyValidationResult,
|
|
224
|
+
type Rule, type AsyncRule, type ValidationDefinition, type ValidationResult,
|
|
46
225
|
} from '@reformer/core/validation';
|
|
47
|
-
// Фабрики-валидаторы
|
|
226
|
+
// Фабрики-валидаторы — правила значения `Rule<T>`, пустое значение пропускают:
|
|
48
227
|
import { required, email, min, minLength, pattern } from '@reformer/core/validators';
|
|
49
|
-
//
|
|
228
|
+
// Тексты замечаний — словарь формы:
|
|
229
|
+
import { defineIssueMessages } from '@reformer/core/i18n';
|
|
230
|
+
// Поведение (отдельный слой):
|
|
50
231
|
import { defineFormBehavior, compute, onChange, revalidateWhen } from '@reformer/core/behaviors';
|
|
232
|
+
// Компонент массива — FieldArray:
|
|
233
|
+
import { FieldArray, useFieldArray } from '@reformer/cdk/field-array';
|
|
51
234
|
```
|
|
52
235
|
|
|
53
236
|
### Schema Common Mistakes
|
|
54
237
|
|
|
55
|
-
Валидация и
|
|
56
|
-
|
|
238
|
+
Валидация и разметка — **разные** контракты. Узел схемы привязывает поле к ручке модели и
|
|
239
|
+
правил не несёт; правила живут в отдельной `defineValidation`.
|
|
57
240
|
|
|
58
241
|
```typescript
|
|
59
|
-
// WRONG -
|
|
242
|
+
// WRONG - запись «имя → узел», литерал вместо ручки и validators прямо в узле
|
|
60
243
|
const schema = {
|
|
61
|
-
name: '', // нет привязки к
|
|
62
|
-
email: { value: '', validators: [required()] }, //
|
|
244
|
+
name: '', // нет привязки к модели
|
|
245
|
+
email: { value: '', validators: [required()] }, // ни ключа value, ни validators у узла нет
|
|
63
246
|
};
|
|
64
247
|
|
|
65
|
-
// CORRECT -
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
248
|
+
// CORRECT - схема только связывает поле с ручкой модели...
|
|
249
|
+
const schema: FormSchemaNode = {
|
|
250
|
+
children: [
|
|
251
|
+
{ model: model.$.name, component: Input, componentProps: { label: 'Name' } },
|
|
252
|
+
{ model: model.$.email, component: Input, componentProps: { label: 'Email' } },
|
|
253
|
+
],
|
|
69
254
|
};
|
|
70
255
|
|
|
71
|
-
// ...а правила — отдельная
|
|
72
|
-
const validation =
|
|
73
|
-
validate(model.$.email, [required({
|
|
256
|
+
// ...а правила — отдельная схема (уходит в createForm({ validation }) либо в validateModel):
|
|
257
|
+
const validation = defineValidation<Form>(({ model }) => {
|
|
258
|
+
validate(model.$.email, [required({ code: 'email.required' }), email()]);
|
|
74
259
|
});
|
|
75
260
|
```
|
|
76
261
|
|
|
@@ -85,7 +270,7 @@ enableWhen(model.$.city, () => Boolean(model.country), { resetOnDisable: true })
|
|
|
85
270
|
```
|
|
86
271
|
|
|
87
272
|
Поведение **не владеет** валидацией. Чтобы поведение инициировало прогон схемы — мост
|
|
88
|
-
`revalidateWhen` (
|
|
273
|
+
`revalidateWhen` (или живая стратегия сборки `validation: { strategy: 'change' }`):
|
|
89
274
|
|
|
90
275
|
```typescript
|
|
91
276
|
// CORRECT - поведение дёргает внешний раннер валидации при изменении зависимости
|
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
4
|
|
|
5
|
-
«Показать поле X только если Y»
|
|
6
|
-
|
|
5
|
+
«Показать поле X только если Y» решается тремя независимыми механизмами — выбирай по тому, что
|
|
6
|
+
именно должно быть условным:
|
|
7
7
|
|
|
8
8
|
1. **Видимость / доступность** (поле остаётся в модели, но выключается/включается и не валидируется)
|
|
9
9
|
— behaviors: `enableWhen`, а для условной записи значения — `compute` / `copyFrom` с опцией `{ when }`.
|
|
10
10
|
2. **Условная валидация** (правила применяются только в истинной ветке) — оператор
|
|
11
|
-
`validateWhen(() => cond, () => {…})` внутри схемы `
|
|
12
|
-
|
|
11
|
+
`validateWhen(() => cond, () => {…})` внутри схемы `defineValidation`; прогон — уровнем формы
|
|
12
|
+
(`bundle.validate()` / `bundle.submission.submit`) либо раннером `validateModel`.
|
|
13
|
+
3. **Скрытие из разметки** (узел вообще не рендерится) — в схеме под рендерером — оператор
|
|
14
|
+
`hideWhen(schema.node(selector), cond)`; при разметке в JSX — `useFormControlValue` + условный
|
|
15
|
+
рендер.
|
|
13
16
|
|
|
14
17
|
> Условная **валидация** — оператор `validateWhen` из `@reformer/core/validation`; старого branch-узла
|
|
15
18
|
> `{ when, children }` и хелпера `applyWhen` больше нет — см.
|
|
@@ -56,36 +59,56 @@ export const creditBehavior = defineFormBehavior<CreditForm>(({ model }) => {
|
|
|
56
59
|
- **Все поля должны быть материализованы в модели** (`createModel`), даже те, что показываются
|
|
57
60
|
только под условием. `enableWhen`/`compute`/`copyFrom` принимают сигналы `model.$.field`, а не
|
|
58
61
|
строковые пути; сигнала для несуществующего поля нет.
|
|
59
|
-
- Поведение подключается к форме через `createForm({
|
|
60
|
-
|
|
62
|
+
- Поведение подключается к форме через `createForm({ …, behavior })` (или
|
|
63
|
+
`createFormFromModel({ model, schema, behavior })`) — форма владеет жизненным циклом,
|
|
64
|
+
DSL-операторы регистрируют свой cleanup сами (ручной массив cleanup'ов не нужен).
|
|
61
65
|
- Таргетом может быть и **группа**: `enableWhen(model.$.residenceAddress, () => model.sameAsRegistration === false)`
|
|
62
66
|
проходит по поддереву; без `resetOnDisable` значение группы сохраняется (удобно, когда оно копируется
|
|
63
67
|
из другого источника).
|
|
64
68
|
|
|
65
|
-
>
|
|
66
|
-
>
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
> `enableWhen` / `disableWhen` существуют только как операторы поведения
|
|
70
|
+
> (`@reformer/core/behaviors`): в корне `@reformer/core` и в `@reformer/core/model` их нет.
|
|
71
|
+
|
|
72
|
+
Скрыть узел разметки, когда форму рисует рендерер, — оператор узла схемы в том же поведении:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { defineFormBehavior, enableWhen, hideWhen } from '@reformer/core/behaviors';
|
|
76
|
+
|
|
77
|
+
export const mortgageBehavior = defineFormBehavior<CreditForm>(({ model, schema }) => {
|
|
78
|
+
const isMortgage = () => model.loanType === 'mortgage';
|
|
79
|
+
enableWhen([model.$.propertyValue, model.$.initialPayment], isMortgage, { resetOnDisable: true });
|
|
80
|
+
hideWhen(schema.node('mortgage'), () => !isMortgage()); // узел с selector: 'mortgage'
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`hideWhen` только записывает правило — исполняет его рендерер; при разметке в JSX он ничего не
|
|
85
|
+
делает (см. раздел «Скрытие в JSX»).
|
|
86
|
+
|
|
87
|
+
**Скрытие — только видимость.** Поля скрытого узла (`hideWhen` или
|
|
88
|
+
`schema.node(sel).setHidden(true)`) остаются включёнными, и правила их проверяют: шаг с
|
|
89
|
+
незаполненным скрытым полем не пройдёт, а замечаний на экране не будет. Поэтому узел с полями
|
|
90
|
+
скрывают вместе с `enableWhen` по тому же условию — как в примере выше.
|
|
69
91
|
|
|
70
92
|
## Условная валидация
|
|
71
93
|
|
|
72
94
|
Правила, действующие только при выполнении условия, оборачиваются оператором
|
|
73
|
-
`validateWhen(() => cond, () => {…})` внутри схемы `
|
|
95
|
+
`validateWhen(() => cond, () => {…})` внутри схемы `defineValidation<T>(({ model }) => …)`.
|
|
74
96
|
Условие `cond` читает модель напрямую (`model.field` — снимок на момент прогона): при истинном условии
|
|
75
97
|
раннер применяет вложенные `validate`/`cross`/…, при ложном — правила ветки **гасятся** (поля,
|
|
76
|
-
затронутые ранее, получают `
|
|
77
|
-
`
|
|
98
|
+
затронутые ранее, получают `setIssues([])`). Прогон — уровнем формы (`createForm({ validation })`,
|
|
99
|
+
затем `bundle.validate()` / `bundle.submission.submit(handler)`) либо раннером
|
|
100
|
+
`validateModel(model, schema): Promise<boolean>`; замечания сами доезжают до нод формы.
|
|
78
101
|
|
|
79
102
|
```typescript
|
|
80
103
|
import {
|
|
81
104
|
validate,
|
|
82
105
|
validateWhen,
|
|
83
|
-
|
|
106
|
+
defineValidation,
|
|
84
107
|
validateModel,
|
|
85
108
|
} from '@reformer/core/validation';
|
|
86
109
|
import { required, min } from '@reformer/core/validators';
|
|
87
110
|
|
|
88
|
-
const schema =
|
|
111
|
+
const schema = defineValidation<CreditForm>(({ model }) => {
|
|
89
112
|
validate(model.$.loanType, [required()]);
|
|
90
113
|
|
|
91
114
|
// ветка применяется только для ипотеки
|
|
@@ -102,19 +125,28 @@ const ok: boolean = await validateModel(model, schema);
|
|
|
102
125
|
```
|
|
103
126
|
|
|
104
127
|
Переключение `loanType` с `mortgage` на другой тип автоматически снимает ошибки `required` с
|
|
105
|
-
`propertyValue`/`initialPayment` — при ложном условии ветка `validateWhen` гасит их (`
|
|
128
|
+
`propertyValue`/`initialPayment` — при ложном условии ветка `validateWhen` гасит их (`setIssues([])`).
|
|
106
129
|
`validateWhen` управляет только **валидацией**; чтобы поля ещё и выпадали из состояния (сброс
|
|
107
130
|
значения), держи их под `enableWhen` из раздела выше — слои независимы.
|
|
108
131
|
|
|
132
|
+
Выключенное поле (`enableWhen`, `disable()`) в проверке **уровня формы** не участвует:
|
|
133
|
+
`bundle.validate`, `validateStep`, `submission.submit`, `flow.next()` и живая проверка гасят его
|
|
134
|
+
замечания, и статус прогона совпадает с `valid` формы. Значение выключенного поля при этом остаётся
|
|
135
|
+
в модели и уходит в обработчик отправки — модель передаётся целиком; убрать его из отправки —
|
|
136
|
+
забота обработчика либо `enableWhen(…, { resetOnDisable: true })`. `validateWhen` нужен, когда
|
|
137
|
+
поле остаётся доступным, но правило по условию не действует, — и в независимом раннере
|
|
138
|
+
`validateModel(model, rules)`, который о нодах не знает.
|
|
139
|
+
|
|
109
140
|
## Скрытие в JSX
|
|
110
141
|
|
|
111
142
|
Чтобы условный блок вообще не рендерился, читай значение-триггер хуком `useFormControlValue(form.field)`
|
|
112
143
|
и делай ранний `return null`. Хук возвращает **только значение** (без деструктуризации `{ value }`).
|
|
113
144
|
|
|
114
145
|
```tsx
|
|
115
|
-
import {
|
|
146
|
+
import type { FormGroup } from '@reformer/core';
|
|
147
|
+
import { useFormControlValue } from '@reformer/core/react';
|
|
116
148
|
|
|
117
|
-
function MortgageFields({ form }: { form: CreditForm }) {
|
|
149
|
+
function MortgageFields({ form }: { form: FormGroup<CreditForm> }) {
|
|
118
150
|
const loanType = useFormControlValue(form.loanType);
|
|
119
151
|
if (loanType !== 'mortgage') return null;
|
|
120
152
|
|
|
@@ -137,7 +169,7 @@ function MortgageFields({ form }: { form: CreditForm }) {
|
|
|
137
169
|
|
|
138
170
|
- **Нет branch-узла `{ when, children }` в дереве схемы.** Старая схема была деревом
|
|
139
171
|
`{ value, validators: [...] }` с ветками `{ when: (scope, root) => boolean, children: [...] }`,
|
|
140
|
-
которое обходил `validateFormModel`. Теперь схема — функция `({ model }) => void`, а условие —
|
|
172
|
+
которое обходил `validateFormModel`. Теперь схема — функция `({ model, cross }) => void`, а условие —
|
|
141
173
|
оператор `validateWhen(() => cond, () => {…})`; отдельного объекта-узла нет.
|
|
142
174
|
|
|
143
175
|
```typescript
|
|
@@ -154,25 +186,32 @@ function MortgageFields({ form }: { form: CreditForm }) {
|
|
|
154
186
|
- **`applyWhen` не существует** — ни как экспорт, ни как локальный сахар: он эмитил branch-узел,
|
|
155
187
|
которого больше нет. Оборачивай условные правила оператором `validateWhen`.
|
|
156
188
|
|
|
157
|
-
- **`ValidationSchemaFn` удалён** (тип старой path-схемы). Тип схемы — `
|
|
158
|
-
(`(
|
|
189
|
+
- **`ValidationSchemaFn` удалён** (тип старой path-схемы). Тип схемы — `ValidationDefinition<T>`
|
|
190
|
+
(`(scope: { model: FormModel<T>; cross }) => void`), фабрика-обёртка — `defineValidation<T>(fn)`.
|
|
159
191
|
|
|
160
|
-
- **Cross-field — обычная функция над
|
|
161
|
-
|
|
162
|
-
оператором `cross(sig,
|
|
192
|
+
- **Cross-field — обычная функция над снимком, а не `ModelValidator (value, scope, root)`.**
|
|
193
|
+
Правило значения — `Rule<T> = (value: T) => error | null`, параметров `scope` / `root` у него нет.
|
|
194
|
+
Соседние поля читаются из снимка модели, а правило вешается оператором `cross(sig, check)` из
|
|
195
|
+
аргумента схемы. Каноничное использование — каталог `validation/` формы `complex-multy-step-form`.
|
|
163
196
|
|
|
164
197
|
```typescript
|
|
165
|
-
import {
|
|
166
|
-
import type {
|
|
198
|
+
import { defineValidation } from '@reformer/core/validation';
|
|
199
|
+
import type { ValidationIssue } from '@reformer/core';
|
|
167
200
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
201
|
+
type Passwords = { password: string; confirmPassword: string };
|
|
202
|
+
|
|
203
|
+
// (snapshot) => error — сравнение с соседним полем
|
|
204
|
+
const passwordsMatch = (passwords: Passwords): ValidationIssue | null =>
|
|
205
|
+
passwords.confirmPassword &&
|
|
206
|
+
passwords.password &&
|
|
207
|
+
passwords.confirmPassword !== passwords.password
|
|
208
|
+
? { code: 'confirmPassword.mismatch' } // текст кода — в словаре формы
|
|
172
209
|
: null;
|
|
173
210
|
|
|
174
|
-
//
|
|
175
|
-
|
|
211
|
+
// cross — из аргумента схемы; check получает снимок модели области:
|
|
212
|
+
const passwordRules = defineValidation<Passwords>(({ model, cross }) => {
|
|
213
|
+
cross(model.$.confirmPassword, passwordsMatch);
|
|
214
|
+
});
|
|
176
215
|
```
|
|
177
216
|
|
|
178
217
|
## See also
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
## 16. READING FIELD VALUES (CRITICALLY IMPORTANT)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Значения живут в модели. Есть три контекста чтения: value-доступ модели, сигналы, и React-хуки
|
|
4
|
+
(`@reformer/core/react`).
|
|
4
5
|
|
|
5
6
|
### В React-компоненте — хуки
|
|
6
7
|
|
|
7
8
|
```typescript
|
|
8
|
-
// Полное состояние поля (объект)
|
|
9
|
-
|
|
9
|
+
// Полное состояние поля (объект); текст замечания — useIssueMessage(messages)(issues[0])
|
|
10
|
+
// shouldShowIssues — показывать ли тексты замечаний; shouldShowError — aria-invalid
|
|
11
|
+
const { value, issues, disabled, touched, shouldShowIssues } = useFormControl(control.email);
|
|
10
12
|
|
|
11
13
|
// Только значение (напрямую, БЕЗ деструктуризации!)
|
|
12
14
|
const email = useFormControlValue(control.email);
|
|
@@ -15,6 +17,16 @@ const email = useFormControlValue(control.email);
|
|
|
15
17
|
const count = useArrayLength(control.items);
|
|
16
18
|
```
|
|
17
19
|
|
|
20
|
+
### Вне React — нода формы
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
form.email.value.value; // сигнал значения ноды (реактивно в effect/computed)
|
|
24
|
+
form.email.getValue(); // значение без подписки
|
|
25
|
+
form.email.issues.value; // замечания: ValidationIssue[] — код и параметры, без текста
|
|
26
|
+
form.items.length; // число строк массива (число; в эффекте подписывает)
|
|
27
|
+
form.getValue(); // значение группы целиком
|
|
28
|
+
```
|
|
29
|
+
|
|
18
30
|
### Вне React — модель
|
|
19
31
|
|
|
20
32
|
```typescript
|
|
@@ -29,9 +41,38 @@ model.$.email.peek(); // нереактивный снимок
|
|
|
29
41
|
model.$.address.city.value; // сигнал вложенного поля (≡ model.address.$.city у под-модели)
|
|
30
42
|
|
|
31
43
|
// весь объект
|
|
32
|
-
model.get(); // снимок { email, address: { city }, ... } — для submit
|
|
44
|
+
model.get(); // НЕреактивный снимок { email, address: { city }, ... } — для submit
|
|
33
45
|
```
|
|
34
46
|
|
|
47
|
+
> ⚠️ `model.get()` и `model.isDirty()` читают через `peek()` — внутри `effect`/`computed` они НЕ
|
|
48
|
+
> создают зависимостей. `computed(() => model.get())` никогда не пересчитается. Для реактивного
|
|
49
|
+
> чтения всего объекта — узел дерева `$` (ниже).
|
|
50
|
+
|
|
51
|
+
### Подписка на группу и на модель целиком
|
|
52
|
+
|
|
53
|
+
Каждый узел дерева `$` — сигнал: лист отдаёт `PathAwareSignal`, а корень, вложенные объекты-группы
|
|
54
|
+
и массивы — `ReadonlySignal` агрегированного значения поддерева. Поэтому подписаться можно на любом
|
|
55
|
+
уровне, а не только на конкретном поле.
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
model.$.subscribe((all) => autosave(all)); // любое изменение модели; all: T целиком
|
|
59
|
+
model.$.address.subscribe((addr) => ...); // только поддерево address
|
|
60
|
+
model.$.address.value; // реактивный снимок группы
|
|
61
|
+
model.$.address.peek(); // нереактивный снимок группы
|
|
62
|
+
model.$.items.subscribe((rows) => ...); // массив: и правка элемента, и push/removeAt/move
|
|
63
|
+
|
|
64
|
+
// узел — обычный ReadonlySignal, поэтому принимается операциями слоя данных
|
|
65
|
+
watchField(model.$.address, (addr) => geocode(addr));
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Дети узла доступны как раньше — `model.$.address.city` по-прежнему сигнал поля. Подписчик вызывается
|
|
69
|
+
сразу с текущим значением (семантика `Signal.subscribe`), а `model.set(...)`/`model.reset()`
|
|
70
|
+
уведомляют один раз, а не по разу на поле.
|
|
71
|
+
|
|
72
|
+
> ⚠️ Доступ к полю выигрывает у свойства сигнала: если в форме есть поле с именем `value`, `peek`,
|
|
73
|
+
> `subscribe`, `valueOf`, `toString`, `toJSON` или `brand`, то `model.$.<группа>.<это имя>` вернёт
|
|
74
|
+
> сигнал поля. `subscribe` при этом продолжает работать.
|
|
75
|
+
|
|
35
76
|
### В behaviors — читаем model напрямую
|
|
36
77
|
|
|
37
78
|
`compute`/`onChange`/условия `when` читают значения из value-модели (`model.field`) —
|