Vue.js предоставляет мощные возможности для создания интерактивных
пользовательских интерфейсов, и формы играют здесь ключевую роль.
Встроенные элементы <input>,
<select>, <textarea> покрывают
базовые сценарии, но при разработке сложных приложений часто требуется
создавать кастомные компоненты форм, обеспечивающие
переиспользуемость, валидируемость и интеграцию с реактивной системой
Vue.
Базовая структура кастомного компонента формы во Vue выглядит следующим образом:
<template>
<div class="form-field">
<label :for="id">{{ label }}</label>
<input
:id="id"
:type="type"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</div>
</template>
<script>
export default {
name: 'CustomInput',
props: {
modelValue: String,
label: String,
type: {
type: String,
default: 'text'
},
id: {
type: String,
required: true
}
}
}
</script>
Ключевые моменты:
v-model через modelValue и
событие update:modelValue обеспечивает двустороннюю
привязку данных.props,
что повышает его переиспользуемость.@input позволяют родительскому компоненту
контролировать значение поля.Кастомный компонент может быть расширен для поддержки нескольких типов данных:
<template>
<div class="form-field">
<label :for="id">{{ label }}</label>
<component
:is="inputType"
:id="id"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
></component>
</div>
</template>
<script>
export default {
props: {
modelValue: [String, Number, Boolean],
label: String,
type: {
type: String,
default: 'text'
},
id: {
type: String,
required: true
}
},
computed: {
inputType() {
if (this.type === 'textarea') return 'textarea';
if (this.type === 'checkbox') return 'input';
return 'input';
}
}
}
</script>
checkbox и radio требуется
дополнительная обработка checked вместо
value.number могут использовать
v-model.number или ручное преобразование через
parseFloat/parseInt.Валидация — важная часть компонентов форм. Vue позволяет интегрировать проверку данных прямо в компонент:
<template>
<div class="form-field">
<label :for="id">{{ label }}</label>
<input
:id="id"
:type="type"
:value="modelValue"
@input="handleInput"
/>
<span v-if="errorMessage" class="error">{{ errorMessage }}</span>
</div>
</template>
<script>
export default {
props: {
modelValue: String,
label: String,
id: String,
rules: {
type: Array,
default: () => []
}
},
data() {
return {
errorMessage: ''
};
},
methods: {
handleInput(event) {
const value = event.target.value;
this.$emit('update:modelValue', value);
this.validate(value);
},
validate(value) {
this.errorMessage = '';
for (const rule of this.rules) {
const result = rule(value);
if (result !== true) {
this.errorMessage = result;
break;
}
}
}
}
}
</script>
rules — массив функций, возвращающих true
при успешной проверке или текст ошибки.v-modelVue 3 позволяет создавать универсальные формы, где
кастомные компоненты работают с v-model так же, как
стандартные элементы:
<CustomInput v-model="formData.username" label="Имя пользователя" id="username" />
<CustomInput v-model="formData.password" type="password" label="Пароль" id="password" />
formData хранится объект с ключами, соответствующими
каждому полю.Для крупных форм часто создают обертки, которые объединяют несколько кастомных компонентов и управляют их состоянием:
<template>
<form @submit.prevent="handleSubmit">
<CustomInput v-model="form.username" label="Логин" id="login" :rules="[required]" />
<CustomInput v-model="form.email" label="Email" id="email" :rules="[required, email]" />
<button type="submit">Отправить</button>
</form>
</template>
<script>
import CustomInput from './CustomInput.vue';
export default {
components: { CustomInput },
data() {
return {
form: {
username: '',
email: ''
}
};
},
methods: {
handleSubmit() {
console.log('Данные формы:', this.form);
},
required(value) {
return value ? true : 'Поле обязательно';
},
email(value) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(value) ? true : 'Неверный формат email';
}
}
}
</script>
Vue обеспечивает удобные способы динамически добавлять или удалять поля формы:
<template>
<div v-for="(field, index) in fields" :key="index">
<CustomInput v-model="field.value" :label="field.label" :id="'field-' + index" />
<button @click="removeField(index)">Удалить</button>
</div>
<button @click="addField">Добавить поле</button>
</template>
<script>
import CustomInput from './CustomInput.vue';
export default {
components: { CustomInput },
data() {
return {
fields: [
{ label: 'Поле 1', value: '' }
]
};
},
methods: {
addField() {
this.fields.push({ label: `Поле ${this.fields.length + 1}`, value: '' });
},
removeField(index) {
this.fields.splice(index, 1);
}
}
}
</script>
fields позволяет
создавать динамические формы с любым количеством полей.Кастомные компоненты форм легко интегрируются с Vuex или Composition API для управления глобальным состоянием:
<script setup>
import { reactive } from 'vue';
import CustomInput from './CustomInput.vue';
const formState = reactive({
name: '',
email: ''
});
</script>
<template>
<CustomInput v-model="formState.name" label="Имя" id="name" />
<CustomInput v-model="formState.email" label="Email" id="email" />
<pre>{{ formState }}</pre>
</template>
formState обновляется автоматически
при изменении значений в компонентах.Кастомные компоненты форм во Vue.js обеспечивают гибкость, удобство переиспользования и полное соответствие реактивной модели. При правильной организации они становятся фундаментом для сложных и масштабируемых приложений.