Skip to content

Dynamic Form ​

Генерируйте формы по JSON-схеме полей. Поддерживает вложенные модели, маски, ввод по регулярному выражению, валюту, выбор даты и все компоненты ввода VeloxUI — внутри всё работает на VxForm.

Nested Model ​

Пути с точечной нотацией, например user.username, автоматически создают вложенные реактивные объекты.

Optional, max 200 characters
vue
<script setup>
const formRef = ref()
const model = reactive({
  user: { username: '', password: '' },
  description: '',
})

const fields = [
  {
    field: 'user.username',
    label: 'Username',
    placeholder: 'Enter username',
    rules: [
      { required: true, message: 'Username is required' },
      { min: 3, max: 20, message: '3-20 characters' },
      { type: 'alphanumeric', message: 'Letters and numbers only' },
    ],
  },
  {
    field: 'user.password',
    label: 'Password',
    type: 'password',
    placeholder: 'Enter password',
    rules: [{ required: true }, { min: 8, message: 'At least 8 characters' }],
  },
  {
    field: 'description',
    label: 'Description',
    type: 'textarea',
    rows: 3,
    help: 'Optional, max 200 characters',
    rules: [{ max: 200, message: 'Max 200 characters' }],
  },
]
</script>

<template>
  <VxDynamicForm ref="formRef" :fields="fields" :model="model">
    <template #actions="{ validate, reset }">
      <VxButton @click="validate()">Submit</VxButton>
      <VxButton variant="outline" @click="reset()">Reset</VxButton>
    </template>
  </VxDynamicForm>
</template>

All Field Types ​

Все поддерживаемые типы ввода в одной форме: text, password, textarea, mask, regex, currency, autocomplete, select, segment, radio, checkbox, switch, slider, rating, date, date-range, date-time, time, color.

0 / 300
Only hex characters allowed
Select a role
4
–
#6366f1
vue
<script setup>
const fields = [
  // ── Text Inputs ──
  { field: 'name', label: 'Full Name', clearable: true, trim: true, rules: { required: true } },
  { field: 'email', label: 'Email', type: 'email', clearable: true, rules: [{ required: true }, { type: 'email' }] },
  { field: 'password', label: 'Password', type: 'password', rules: [{ required: true }, { min: 8 }] },
  { field: 'bio', label: 'Bio', type: 'textarea', rows: 3, maxlength: 300, showWordLimit: true },

  // ── Masked Inputs ──
  { field: 'phone', label: 'Phone', type: 'tel', mask: '+# (###) ###-####' },
  { field: 'creditCard', label: 'Credit Card', mask: '#### #### #### ####' },
  { field: 'zipCode', label: 'Zip Code', mask: '#####' },
  { field: 'ssn', label: 'SSN', mask: '###-##-####' },

  // ── Regex-Restricted Input ──
  { field: 'hexCode', label: 'Hex Code', regex: /^#?[0-9A-Fa-f]{0,6}$/ },

  // ── Currency ──
  { field: 'salary', label: 'Salary (USD)', type: 'currency', currencyOptions: { prefix: '$ ', thousands: ',' } },
  { field: 'price', label: 'Price (EUR)', type: 'currency', currencyOptions: { prefix: '€ ', decimal: ',' } },

  // ── Autocomplete ──
  { field: 'city', label: 'City', type: 'autocomplete', options: ['New York', 'LA', 'Chicago', ...] },

  // ── Select ──
  { field: 'role', label: 'Role', type: 'select', options: [...], rules: { required: true } },

  // ── Segment ──
  { field: 'plan', label: 'Plan', type: 'segment', options: [...], defaultValue: 'free' },

  // ── Radio Group ──
  { field: 'gender', label: 'Gender', type: 'radio-group', options: [...] },

  // ── Checkbox Group ──
  { field: 'skills', label: 'Skills', type: 'checkbox-group', options: [...], defaultValue: [] },

  // ── Switch ──
  { field: 'newsletter', label: 'Newsletter', type: 'switch', defaultValue: true },

  // ── Checkbox ──
  { field: 'terms', label: 'Agreement', type: 'checkbox', placeholder: 'I accept the terms' },

  // ── Slider ──
  { field: 'experience', label: 'Experience', type: 'slider', props: { min: 0, max: 20, showInput: true } },

  // ── Rating ──
  { field: 'satisfaction', label: 'Satisfaction', type: 'rating', props: { allowHalf: true, showValue: true } },

  // ── Date & Time ──
  { field: 'birthday', label: 'Birthday', type: 'date', clearable: true },
  { field: 'vacation', label: 'Vacation Range', type: 'date-range', defaultValue: [] },
  { field: 'appointment', label: 'Appointment', type: 'date-time', clearable: true },
  { field: 'wakeUp', label: 'Wake Up Time', type: 'time', clearable: true },

  // ── Color Picker ──
  { field: 'theme', label: 'Theme Color', type: 'color', defaultValue: '#6366f1', props: { showInput: true } },
]
</script>

Grid Layout ​

Используйте columns и span у конкретного поля для многоколоночных форм. Маски и поля валюты тоже работают в сетке.

Select
vue
<script setup>
const fields = [
  { field: 'firstName', label: 'First Name', rules: { required: true } },
  { field: 'lastName', label: 'Last Name', rules: { required: true } },
  { field: 'email', label: 'Email', type: 'email' },
  { field: 'phone', label: 'Phone', type: 'tel', mask: '+# (###) ###-####' },
  { field: 'salary', label: 'Salary', type: 'currency', currencyOptions: { prefix: '$ ' } },
  { field: 'department', label: 'Department', type: 'select', options: [...] },
  { field: 'startDate', label: 'Start Date', type: 'date' },
  { field: 'level', label: 'Level', type: 'segment', options: [...] },
  { field: 'notes', label: 'Notes', type: 'textarea', rows: 3, span: 2 }, // spans 2 columns
  { field: 'active', label: 'Active', type: 'switch', defaultValue: true },
]
</script>

<template>
  <VxDynamicForm :fields="fields" :columns="2" :gutter="20">
    <template #actions><VxButton>Submit</VxButton></template>
  </VxDynamicForm>
</template>

Conditional Fields ​

Используйте hidden: (model) => boolean, чтобы реактивно показывать и скрывать поля.

vue
<script setup>
const fields = [
  {
    field: 'accountType', label: 'Account Type', type: 'segment',
    options: [{ label: 'Personal', value: 'personal' }, { label: 'Business', value: 'business' }],
    defaultValue: 'personal',
  },
  { field: 'fullName', label: 'Full Name', hidden: (m) => m.accountType === 'business' },
  { field: 'companyName', label: 'Company', hidden: (m) => m.accountType !== 'business' },
  { field: 'taxId', label: 'Tax ID', mask: '##-#######', hidden: (m) => m.accountType !== 'business' },
  { field: 'email', label: 'Email', type: 'email' },
]
</script>

JSON from API ​

Загружайте схему полей динамически и мгновенно отрисовывайте форму.

vue
<script setup>
const schema = ref(null)

// Fetched from API — all fields are plain JSON-serializable objects
// {
//   field: 'ticket.title', label: 'Title',
//   rules: [{ required: true, message: 'Title is required' }]
// }

async function loadSchema() {
  schema.value = await fetch('/api/form-schema').then(r => r.json())
}
</script>

<template>
  <VxButton v-if="!schema" @click="loadSchema">Load Schema</VxButton>
  <VxDynamicForm v-if="schema" :fields="schema">
    <template #actions="{ validate }">
      <VxButton @click="validate()">Submit</VxButton>
    </template>
  </VxDynamicForm>
</template>

Custom Field Slot ​

Переопределите отрисовку любого поля через именованный слот, соответствующий ключу поля.

vue
<template>
  <VxDynamicForm :fields="fields">
    <!-- Override 'avatar' field with custom component -->
    <template #avatar="{ value, set }">
      <MyAvatarUploader :value="value" @change="set" />
    </template>

    <template #actions="{ validate }">
      <VxButton @click="validate()">Submit</VxButton>
    </template>
  </VxDynamicForm>
</template>

Props ​

PropTypeDefaultDescription
fieldsDynamicFormField[]обязательныйМассив схемы полей
modelRecord<string, any>—Реактивная модель (создаётся автоматически, если не указана)
rulesFormRules—Правила уровня формы (объединяются с правилами полей)
columnsnumber0Колонки сетки (0 — без сетки)
gutternumber16Промежуток сетки в пикселях
layout'vertical' | 'horizontal' | 'inline''vertical'Раскладка формы
labelWidthstring | number—Ширина метки для горизонтальной раскладки
labelAlign'left' | 'right''right'Выравнивание текста метки
labelSuffixstring''Суффикс после меток
size'sm' | 'default' | 'lg''default'Размер формы
disabledbooleanfalseОтключить всю форму
loadingbooleanfalseСостояние загрузки
showRequiredMarkbooleantrueПоказывать звёздочку обязательности
requiredMarkPosition'left' | 'right''left'Позиция звёздочки
validateOnChangebooleantrueАвтовалидация при изменении
validateOnBlurbooleantrueАвтовалидация при потере фокуса
hideErrorMessagebooleanfalseСкрыть сообщения об ошибках
scrollToErrorbooleanfalseПрокручивать к первой ошибке
statusIconbooleanfalseПоказывать иконки состояния
showAllErrorsbooleanfalseПоказывать все ошибки правил

DynamicFormField ​

PropertyTypeDescription
fieldstringКлюч поля — поддерживает точечную нотацию (user.name)
labelstringТекст метки
typeDynamicFormFieldTypeТип ввода (см. таблицу ниже)
placeholderstringТекст плейсхолдера
rulesFormRule | FormRule[]Правила валидации
requiredbooleanПринудительно показывать метку обязательности
helpstringПодсказка под полем
descriptionstringПодсказка на метке
dependenciesstring[]Повторная валидация при изменении этих полей
defaultValueanyЗначение по умолчанию
options{ label, value }[]Для select, radio, checkbox, segment, autocomplete, transfer
rowsnumberКоличество строк textarea
disabledbooleanОтключить это поле
readonlybooleanСделать это поле только для чтения
hiddenboolean | (model) => booleanУсловное скрытие
spannumberСколько колонок занимает поле в режиме сетки
maskstringШаблон маски ввода (###-####, +# (###))
regexRegExpОграничить ввод соответствиями регулярному выражению
clearablebooleanПоказывать кнопку очистки
maxlengthnumberМаксимальная длина в символах
showWordLimitbooleanПоказывать счётчик символов
trimbooleanАвтоматически удалять пробелы по краям
lazybooleanОбновлять только при потере фокуса
debouncenumberЗадержка ввода (мс)
currencyOptionsobjectФормат валюты { prefix, suffix, decimal, thousands, precision }
propsRecord<string, any>Прокидывается в нижележащий компонент
componentComponentПользовательский компонент (при type: 'custom')

Field Types ​

TypeComponentNotes
text (по умолчанию)VxInputПоддерживает mask, regex, trim, lazy, debounce
passwordVxInput type="password"
textareaVxInput type="textarea"Поддерживает rows, maxlength, showWordLimit
numberVxInput
emailVxInput
searchVxInput type="search"
telVxInputЛучше использовать с mask для телефонных масок
urlVxInput
selectVxSelectНужны options
checkboxVxCheckboxОдиночный булев; placeholder используется как метка
checkbox-groupVxCheckboxGroupНужны options
radio / radio-groupVxRadioGroupНужны options
switchVxSwitch
sliderVxSliderПередавайте min, max, showInput через props
ratingVxRatingПередавайте allowHalf, showValue через props
dateVxDatePicker
date-rangeVxDateRangePicker
date-timeVxDateTimePicker
timeVxTimePicker
colorVxColorPickerПередавайте showInput, presets через props
currencyVxCurrencyInputНастраивается через currencyOptions
autocompleteVxAutocompleteПринимает string[] или объекты-опции
segmentVxSegmentНужны options
transferVxTransferНужны options (в качестве data)
uploadVxUploadНастраивается через props
customВаш компонентЗадайте проп component

Exposed Methods ​

MethodSignatureDescription
validate() => Promise<{ valid, errors }>Валидировать все поля
validateFields(fields: string[]) => Promise<{ valid, errors }>Валидировать конкретные поля
resetFields() => voidСбросить состояние валидации
clearValidation(fields?: string[]) => voidОчистить ошибки
getModel() => Record<string, any>Глубокая копия текущей модели

Slots ​

SlotPropsDescription
actions{ validate, reset, model }Действия формы (кнопки)
[field]{ field, value, set }Переопределить отрисовку любого поля