你好! Меня зовут Дмитрий, я фронтенд-разработчик в компании fuse8. Сегодня мы рассмотрим, как можно проводить валидацию кастомных компонентов в формах из UI-библиотеки ElementPlus.
Если вы работали с формами в ElementPlus, то наверняка знаете, что библиотека предоставляет простой интерфейс для валидации. Но что делать, если в форме используется кастомный компонент и необходимо применить правило валидации, которое передали в форму? С этим и разберёмся.
Базовый пример валидации из документации
Начнем с простого примера, чтобы освежить понимание общего механизма валидации, который описан в документации ElementPlus.
<template> <div class="test"> <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-position="top" @submit.prevent="submitForm"> <el-form-item label="Activity name" prop="name"> <el-input v-model="ruleForm.name" clearable /> </el-form-item> <button type="submit">Send</button> </el-form> </div> </template> <script setup> import { ref } from 'vue'; const ruleFormRef = ref(); //Данные формы const ruleForm = ref({ name: '', }); //Правила валидации const rules = { name: [ { required: true, message: 'Please input Activity name', trigger: 'change' } ], }; //Применение валидации const submitForm = async () => { if (!ruleFormRef.value) { return; } await ruleFormRef.value.validate((valid, fields) => { if (valid) { console.log('submit!'); } else { console.log('error submit!', fields); } }); }; </script>
-
el-form — компонент формы, который ожидает модель формы (ruleForm) и правила обработки полей (ruless).
-
el-form-item — дочерний компонент, который использует prop для связи с моделью формы.
-
el-input — стандартный компонент ввода из ElementUI.
При нажатии на кнопку Send вызывает submitForm(), где вызывается валидация из компонента el-form, для каждого правила описанного в rules.
Проблема: валидация с кастомным компонентом
Если попытаться отправить пустую форму из примера выше, библиотека подсветит поле и выведет сообщение об ошибке.
Если начнем вводить данные, то подсветка пропадёт.
— Круто?
— Круто!
Но что будет, если у нас есть какой-то кастомный компонент или простой инпут с двухсторонним связыванием?
... <el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-position="top" @submit.prevent="submitForm"> <el-form-item label="Activity name" prop="name"> <input type="text" v-model="ruleForm.name"/> // стандартный html тег input </el-form-item> <button type="submit">Send</button> </el-form> ...
И тут, если мы попытаемся отправить пустую форму, то валидация сработает – вызовется функция submitForm(), и пустое поле подсветится. Однако, если мы начнем вводить текст, то подсветка никуда не денется. И да, простите, что не стал заморачиваться со стилями!
В общем, именно с такой проблемой я столкнулся — вроде на уровне формы валидация есть, а вот на уровне компонента нет. В документации описания того, как это можно победить я не нашел (может плохо искал).
Поиск решения
Вооружившись тяжелым, пристальным взглядом и банкой несквика, пошел в исходники. Если коротко, то внутри компонента el-form генерируется контекст из всего того, что мы передали, и, используя механизм Provide / inject, пробрасывается потомкам. То же самое происходит и в elFormItem. Ключами в provide выступают символы:
-
formContextKey — символ контекста формы
Слава Линуксу, создатели ElementPlus заботливо дали возможность импортировать эти символы напрямую из elements-plus.
В сгенерированном контексте нас интересует функция validate(), которая, как нетрудно догадаться, вызывает правило валидации для конкретного поля (prop=”name”).
Теперь дело осталось за малым, делаем watch за изменениями элементов, проводим inject контекста и вызываем валидацию нашего поля при изменении.
<-- CustomInput.vue --> <template> <input v-model="model" /> </template> <script setup> import { inject, watch } from 'vue'; import { formItemContextKey } from 'element-plus'; const model = defineModel(); //модный сахарок из Vue 3.4 const elFormItem = inject(formItemContextKey, undefined); //берем контекст //Следим за изменением данных компонента и применяем валидацию watch(model, () => { elFormItem?.validate?.('change'); }); </script>
Теперь наш компонент реагирует на изменение данных и применяет валидацию.
Таким образом можно интегрировать ваши компоненты в механизм валидации, который предоставляет ElementPlus. Возможно, есть более изящный вариант, но я его не нашел. Предлагаю вам поделиться в комментариях.
PS: Все, конечно же, поняли отсылку в начале — типа поздоровался по-китайски и библиотека китайская. Шучу. Был в Китае, выучил 2 слова, не мог не блеснуть =)
ссылка на оригинал статьи https://habr.com/ru/articles/867148/
Добавить комментарий