Не удается грамотно провалидировать компонент инпута vue2, расхождение value у input и родителя
Имеется проблема следующего характера, есть компонент инпута в который через :value передается начальное значение (приходит с бэка), а через @input присваивается новое значение, стоит цель запретить ввод символов, указанных в регулярном выражении (убрать визуальное отображение).
Родительский компонент
<template>
<TheInput class="mb-1" label="Имя" :value="userProfile.name" @input="test($event)" />
</template>
<script>
export default {
data() {
return {
userProfile: {
name: null,
}
}},
methods:{
test(evt) {
console.log(1, evt, this.userProfile.name);
const regExp = /[0-9]/g;
this.userProfile.name = evt.replace(regExp, "");
console.log(2, evt, this.userProfile.name);
},
}
}
</script>
Компонент инпута
<template>
<div class="admin-wrapper" :class="{ 'admin-attention': attention }">
<label for="input" class="admin-label" v-if="label">{{ label }}</label>
<div class="admin-input-container d-flex align-center">
<input
autocomplete="off"
class="admin-input"
id="input"
v-bind="$props"
@input="onInput($event.target.value)"
@blur="checkValueInput"
/>
<IconCross
class="admin-button"
v-if="value"
@click.native="$emit('input', null)"
:fill="fillCross"
/>
</div>
<slot />
</div>
</template>
<script>
export default {
name: "TheInput",
components: { IconCross, IconWarning },
props: {
value: {
type: [String, Number],
default: "",
},
type: {
type: String,
default: "text",
},
placeholder: String,
label: String,
},
methods: {
onInput(evt){
console.log('val input',evt);
this.$emit('input', evt)
},
}
};
</script>
Дочка просто emit-ит новое value к родителю, где вызывается метод в котором лежит регулярное выражение, фильтрующее по нужному принципу, но по факту в разметке напечатанное выражение не стирается, пока не введешь любой символ, который подходит по регулярке. Как сделать так, чтоб значение в нативном input и в родителе было идентично?
Ожидаю получить отсутствие ввода определенных символов-цифр, внутри скрипта отрабатывает как нужно, внешне имеются расхождения.
- подстрока 1(значение из инпута), подстрока 2 значение userProfile.name (передается через :value ))
Ответы (2 шт):
TL;DR; это не вы, это Vue. Не изобретайте велосипед, используйте готовое.
Проблема в том, хоть значение, хранящееся в переменной, и изменяется корректно, это все равно не ведет к обновлению значения в инпуте. Не уверен, чьи это механики, но, скорее всего, так работает Vue.
Проблему можно решить 3 способами:
Изменять
key
у инпута при изменении значения. Таким образом инпут будет перерисовываться и, следовательно, значение самого инпута будет обновлено (и, поэтому, не будут показаны цифры уже после того как они были удалены из строки).Пихать руками обновленное значение в аттрибут
value
инпута (послеreplace
'а). Грубо говоря, то же самое. Триггерим перерисовку компонента, только на этот раз менее костыльным способом.
В первом и во втором случае будет проблема с фокусом и/или прыгающим курсором. Если говорить более конкретно, то в первом случае будет слетать фокус с поля, и вам придется возвращать его руками, а во втором случае (и в первом тоже, если возвращать фокус вручную), если набрать цифру где-нибудь в середине строки, то курсор улетит в конец.
Поэтому, есть 3 вариант: использовать готовую библиотеку, где подобное уже реализовано. Таких библиотек ну примерно сотни. Конкретную давать не буду, найдете сами по словам vue, input, regex, mask, validation и т.д. в разных комбинациях.
Неоптимальное, но решение
<input
autocomplete="off"
class="admin-input"
id="input"
ref="input"
v-bind="$props"
@input="onInput($event.target.value)"
@blur="checkValueInput"
/>
onInput(evt) {
const input = this.$refs.input;
const regExp = /[0-9]/img;
const newVal = evt.replace(regExp, '');
input.value = newVal;
this.$emit('input', newVal);
},
Данный вариант будет улучшен передачей пропса с валидацией для разделения кода, уменьшив количество вычислений.
Модернизированный вариант с пропсом
// Разметка в родителе
<TheInput class="mb-1" label="Имя" v-model="userProfile.name" :regExp="/[a-z]/gi" />
// Разметка в дочке
<input
autocomplete="off"
class="admin-input"
id="input"
:value="value"
:type="type"
:min="min"
:max="max"
:minlength="minlength"
:maxlength="maxlength"
:placeholder="placeholder"
@input="onInput"
@blur="checkValueInput"
/>
//Входной параметр (prop)
regExp: {
type: RegExp,
default: null
},
//Метод
onInput($event) {
const target = $event.target;
if (this.regExp) {
target.value = target.value.replace((this.regExp), '');
}
this.$emit('input', target.value);
Саму регулярку можно вынести в отдельный слой со списком регулярок, откуда тянуть функцией с параметром, смотря какая понадобится.