Стандартная проблема с кастомными инпутами и валидацией в форме.
Одно из решений - в форме создать один массив ошибок с поинтерами, указывающими на определенный input. Особенно это хорошо, когда бекенд на этапе проверки формы возвращает ошибки тоже с конкретными поинтерами.
Пример кастомного инпута c выводом текста ошибки под ним. Если есть хоть одна ошибка, то навешивается "ошибочный" класс со своими стилями.
<template>
<div class="input">
<input
type="text"
class="input__field"
:class="{ 'input__field_error': errors.length > 0 }"
:placeholder="placeholder"
:value="value"
:disabled="disabled"
@input="handleInput"
>
<div class="input__errors">
<p
v-for="(error, index) in errors"
:key="index"
class="input__error"
>
{{ error }}
</p>
</div>
</div>
</template>
<script>
export default {
name: 'custom-input',
props: {
disabled: {
type: Boolean,
default: false,
},
errors: {
type: Array,
default: () => ([]),
},
value: {
type: String,
default: '',
},
placeholder: {
type: String,
default: '',
},
},
methods: {
handleInput({ target: { value } }) {
this.$emit('onInput', value);
},
},
};
</script>
А в форме уже мы распределяем все ошибки по нужным инпутам:
<template>
<form @onSubmit.prevent="handleSubmit">
<custom-input
:value="phone"
:disabled="loading"
:errors="phoneErrors" // передаем только относящиеся к конкретному инпуту ошибки
@onInput="handleInput"
/>
<button type="submit">Отправить</button>
</form>
</template>
<script>
export default {
data: () => ({
phone: '',
errors: [],
loading: false,
}),
computed: {
phoneErrors() {
// этот код лучше вынести в хелпер, чтобы потом переиспользовать с другими поинтерами
if (this.errors.length === 0) return [];
return this.errors
.filter(error => error.pointer === 'phone') // проверка на нужный поинтер
.map(item => item.detail); // делаем массив только из текста ошибок
},
},
methods: {
handleInput(newPhone) {
// при вводе нового значения обнуляем все ошибки.
// Можно сделать обнуление ошибок по конкретному поинтеру
this.errors = [];
this.phone = newPhone;
},
handleSubmit() {
this.errors = [];
// Если инпут пустой, то сами генерируем ошибку
// и не делаем отправку данных на сервер
if (this.phone.length === 0) {
this.errors = [...this.errors, {
detail: 'Телефон не может быть пустым',
pointer: 'phone',
}];
return;
}
// Если же все ок, то отправляем данные на сервер
this.loading = true;
this.sendPhone();
},
},
};
</script>
Возможно это и многословный код, но он хорошо читается, понятен и крайне легко покрывается unit тестами