Skip to main content

Ноды и proxy

Ноды — строительные блоки формы. Их не создают вручную через new — их строит createForm поверх сигналов модели, а доступ к ним даёт типизированный proxy.

НодаНазначениеПример
FieldNodeодно значение (строка, число, флаг)текстовое поле, чекбокс
GroupNodeобъект с именованными полямисекция формы, адрес
ArrayNodeдинамический список подформтелефоны, адреса
ModelArrayNodeмассив, привязанный к массиву моделисписок объектов модели

Доступ через proxy

createForm возвращает proxy: к полям обращаются по имени, вложенность — цепочкой.

import { createModel, createForm } from '@reformer/core';
import { Input } from '@reformer/ui-kit';

type Form = { firstName: string; address: { city: string } };

const model = createModel<Form>({ firstName: '', address: { city: '' } });

const schema = {
firstName: { value: model.$.firstName, component: Input },
address: {
city: { value: model.$.address.city, component: Input },
},
};

const form = createForm<Form>({ model, schema });

form.firstName; // FieldNode<string>
form.address; // GroupNode
form.address.city; // FieldNode<string> — вложенный доступ
warning
Proxy не проходит instanceof

Не проверяйте тип ноды через node instanceof FieldNode — proxy это не пройдёт. Используйте type-guards.

Состояние ноды — сигналы

Свойства ноды value, valid, invalid, errors, touched, dirty, disabled, pendingсигналы (реактивные, только для чтения). В React их удобнее читать через useFormControl; вне React — через методы ниже или .value сигнала.

Свойство (сигнал)ТипОписание
valueTтекущее значение
valid / invalidbooleanпроходит / не проходит валидацию
errorsValidationError[]ошибки валидации ([] когда валидно)
touched / dirtybooleanпользователь взаимодействовал / значение менялось
disabledbooleanполе отключено
pendingbooleanидёт асинхронная валидация

Методы ноды

form.firstName.getValue(); // прочитать значение
form.firstName.setValue('John'); // записать значение
form.firstName.patchValue(partial); // частичное обновление (для групп/массивов)
form.firstName.reset(); // сброс к начальному
form.firstName.markAsTouched(); // отметить как «тронутое»
form.firstName.markAsDirty();
form.firstName.disable(); // отключить
form.firstName.enable(); // включить
form.firstName.validate(); // запустить валидацию ноды

Изменение значения через ноду отражается в модели (и наоборот) — это одни и те же сигналы.

FieldNode

Лист дерева. Дополнительно к общим методам:

form.email.updateComponentProps({ options: [...] }); // обновить пропсы компонента (например, опции Select)
form.email.componentProps; // сигнал текущих componentProps
form.email.shouldShowError; // сигнал: показывать ли ошибку (touched + invalid)

GroupNode

Группирует поля в объект.

form.address.getValue(); // { city: '...', zip: '...' }
form.address.touchAll(); // отметить все дочерние поля тронутыми
form.address.getFieldByPath('city'); // доступ по строковому пути

ArrayNode

Динамический список подформ. Сами данные принадлежат модели, поэтому мутации выполняются на модели, а нода отражает их реактивно:

model.phones.push({ type: 'mobile', number: '' }); // добавить элемент
model.phones.removeAt(0); // удалить

form.phones.at(0); // FormProxy элемента
form.phones.map((item, i) => /* ... */); // обход элементов

Type-guards

Поскольку proxy не проходит instanceof, тип ноды определяют через guard-функции:

import { isFieldNode, isGroupNode, isArrayNode, getNodeType } from '@reformer/core';

if (isFieldNode(node)) {
/* лист */
}
if (isGroupNode(node)) {
/* группа */
}
if (isArrayNode(node)) {
/* массив */
}

getNodeType(node); // 'field' | 'group' | 'array'

Дальше