Dynamic Form
JSON maydon sxemasidan formalarni yaratadi. Ichma-ich modellar, masklar, regex kiritishlar, valyuta, sana tanlovchilar va har bir VeloxUI kiritish komponentini qo'llab-quvvatlaydi — barchasi ichida VxForm asosida ishlaydi.
Nested Model
user.username kabi nuqta-yozuvli yo'llar avtomatik ravishda ichma-ich reaktiv obyektlarni yaratadi.
<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
Bitta formada qo'llab-quvvatlanadigan barcha kiritish turlari: text, password, textarea, mask, regex, currency, autocomplete, select, segment, radio, checkbox, switch, slider, rating, date, date-range, date-time, time, color.
<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
Ko'p ustunli formalar uchun columns va har bir maydon uchun span dan foydalaning. Mask va currency grid ichida ham ishlaydi.
<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
Maydonlarni reaktiv tarzda ko'rsatish/yashirish uchun hidden: (model) => boolean dan foydalaning.
<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
Maydon sxemasini dinamik ravishda yuklab, formani darhol render qiling.
<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
Maydon kalitiga mos nomli slot yordamida istalgan maydonning renderini qayta belgilang.
<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
| Prop | Type | Default | Description |
|---|---|---|---|
fields | DynamicFormField[] | required | Maydon sxemasi massivi |
model | Record<string, any> | — | Reaktiv model (agar berilmasa avto-yaratiladi) |
rules | FormRules | — | Forma darajasidagi qoidalar (har bir maydon bilan birlashadi) |
columns | number | 0 | Grid ustunlari (0 = grid yo'q) |
gutter | number | 16 | Grid orasidagi bo'shliq (px) |
layout | 'vertical' | 'horizontal' | 'inline' | 'vertical' | Forma layouti |
labelWidth | string | number | — | Gorizontal layout uchun yorliq kengligi |
labelAlign | 'left' | 'right' | 'right' | Yorliq matn tekislash |
labelSuffix | string | '' | Yorliqlardan keyingi qo'shimcha |
size | 'sm' | 'default' | 'lg' | 'default' | Forma o'lchami |
disabled | boolean | false | Butun formani o'chirish |
loading | boolean | false | Yuklanish holati |
showRequiredMark | boolean | true | Majburiy yulduzchani ko'rsatish |
requiredMarkPosition | 'left' | 'right' | 'left' | Yulduzcha joylashuvi |
validateOnChange | boolean | true | O'zgarishda avto-tekshirish |
validateOnBlur | boolean | true | Blur da avto-tekshirish |
hideErrorMessage | boolean | false | Xato xabarlarini yashirish |
scrollToError | boolean | false | Birinchi xatoga scroll qilish |
statusIcon | boolean | false | Holat ikonkalarini ko'rsatish |
showAllErrors | boolean | false | Barcha qoidalar xatolarini ko'rsatish |
DynamicFormField
| Property | Type | Description |
|---|---|---|
field | string | Maydon kaliti — nuqta yozuvini qo'llab-quvvatlaydi (user.name) |
label | string | Yorliq matni |
type | DynamicFormFieldType | Kiritish turi (pastdagi jadvalga qarang) |
placeholder | string | Placeholder matni |
rules | FormRule | FormRule[] | Tekshirish qoidalari |
required | boolean | Majburiy belgini majburlash |
help | string | Maydon ostidagi yordam matni |
description | string | Yorliqdagi tooltip |
dependencies | string[] | Bular o'zgarganda qayta tekshirish |
defaultValue | any | Sukutdagi qiymat |
options | { label, value }[] | select, radio, checkbox, segment, autocomplete, transfer uchun |
rows | number | Textarea qatorlari |
disabled | boolean | Bu maydonni o'chirish |
readonly | boolean | Bu maydonni faqat o'qish uchun qilish |
hidden | boolean | (model) => boolean | Shart bilan yashirish |
span | number | Grid rejimidagi ustun span i |
mask | string | Kiritish mask shabloni (###-####, +# (###)) |
regex | RegExp | Kiritishni regex moslariga cheklash |
clearable | boolean | Tozalash tugmasini ko'rsatish |
maxlength | number | Maksimal belgi uzunligi |
showWordLimit | boolean | So'z sanagichini ko'rsatish |
trim | boolean | Bo'shliqlarni avto-kesish |
lazy | boolean | Faqat blur da yangilash |
debounce | number | Kiritishni debounce qilish (ms) |
currencyOptions | object | Valyuta formati { prefix, suffix, decimal, thousands, precision } |
props | Record<string, any> | Asosiy komponentga o'tkazish |
component | Component | Maxsus komponent (type: 'custom' bo'lganda) |
Field Types
| Type | Component | Notes |
|---|---|---|
text (default) | VxInput | mask, regex, trim, lazy, debounce ni qo'llab-quvvatlaydi |
password | VxInput type="password" | |
textarea | VxInput type="textarea" | rows, maxlength, showWordLimit ni qo'llab-quvvatlaydi |
number | VxInput | |
email | VxInput | |
search | VxInput type="search" | |
tel | VxInput | Telefon shablonlari uchun mask bilan ishlatish tavsiya etiladi |
url | VxInput | |
select | VxSelect | options kerak |
checkbox | VxCheckbox | Yagona boolean; placeholder yorliq sifatida ishlatiladi |
checkbox-group | VxCheckboxGroup | options kerak |
radio / radio-group | VxRadioGroup | options kerak |
switch | VxSwitch | |
slider | VxSlider | min, max, showInput ni props orqali yuboring |
rating | VxRating | allowHalf, showValue ni props orqali yuboring |
date | VxDatePicker | |
date-range | VxDateRangePicker | |
date-time | VxDateTimePicker | |
time | VxTimePicker | |
color | VxColorPicker | showInput, presets ni props orqali yuboring |
currency | VxCurrencyInput | currencyOptions orqali sozlang |
autocomplete | VxAutocomplete | string[] yoki option obyektlarini yuboring |
segment | VxSegment | options kerak |
transfer | VxTransfer | options kerak (data sifatida) |
upload | VxUpload | props orqali sozlang |
custom | Your component | component prop ni belgilang |
Exposed Methods
| Method | Signature | Description |
|---|---|---|
validate | () => Promise<{ valid, errors }> | Barcha maydonlarni tekshirish |
validateFields | (fields: string[]) => Promise<{ valid, errors }> | Aniq maydonlarni tekshirish |
resetFields | () => void | Tekshirish holatini tiklash |
clearValidation | (fields?: string[]) => void | Xatolarni tozalash |
getModel | () => Record<string, any> | Hozirgi modelning chuqur nusxasi |
Slots
| Slot | Props | Description |
|---|---|---|
actions | { validate, reset, model } | Forma amallari (tugmalar) |
[field] | { field, value, set } | Istalgan maydonning renderini qayta belgilash |