Я пытаюсь прикрепить простой счетчик символов к элементу ввода, но как только я показываю его обратно пользователю, ввод прерывается, и я не могу ввести дополнительные символы в поле ввода.
<template>
<div>
<label class = "label" :class = "{ 'label-large' : large }" v-if = "label">
{{ label }} <sup class = "is-required" v-if = "isRequired">Req</sup>
</label>
<input class = "input-control" :class = "{ 'input-large' : large }" :maxlength = "maxLength" :placeholder = "placeholderText" ref = "input" :value = "text" @change = "formatValue($event.target.value)" @keyup = "countCharacters($event.target.value)" />
<div class = "flex text-x-small-regular mt-2" :class = "large ? 'px-4' : 'px-2'" v-if = "maxLength || validationFailed">
<div class = "validation-message">
<template v-if = "validationFailed">{{ validationMessage }}</template>
</div>
<div class = "character-count" v-if = "maxLength">
<span :class = "characterCountWarningStyle">{{ characterCount }}</span> / {{ maxLength }}
</div>
</div>
</div>
</template>
<script>
export default {
props: {
isRequired: {
default: false,
required: false,
type: Boolean
},
label: {
required: false,
type: String
},
large: {
default: false,
required: false,
type: Boolean,
},
maxLength: {
required: false,
type: Number
},
placeholder: {
required: false,
type: String
},
text: {
required: false,
type: String
},
validationMessage: {
default: "Required field.",
required: false,
type: String
}
},
data() {
return {
characterCount: 0,
validationFailed: false,
value: undefined
}
},
computed: {
characterCountWarningStyle() {
return "" // Simplified.
},
placeholderText() {
return "" // Simplified.
}
},
methods: {
countCharacters(value) {
// Works:
console.info(value.length);
// Breaks form input: this.characterCount = value.length;
},
formatValue(value) {
this.validationFailed = false;
if (value) value = value.trim();
this.validate(value);
},
validate(value) {
if (this.isRequired && !value) {
this.validationFailed = true;
}
this.$emit('update', value);
}
}
}
</script>
Подводя итог приведенному выше коду, я делаю некоторую базовую очистку при изменении и хочу запустить подсчет символов при нажатии клавиши. Что мне не хватает?
Это так странно. Как только я удаляю :value = "text" из HTML, он работает нормально, но как только я добавляю привязку, он начинает блокировать весь ввод. Кстати, я использую Vue 2.6.
На самом деле попробуйте здесь: jsfiddle.net/r46g3nt1
Вы мутируете реквизит. Вот с этого надо начинать..
Извините как? Я тоже пробовал это с вычисляемым сеттером. Та же проблема.
Я не говорил, что это решает вашу проблему. Просто ошибка. Вы можете решить свою проблему, заменив :value
на v-model
.
Вам нужно заменить :value
на v-model
для двусторонней привязки данных, и вместо того, чтобы напрямую изменять свойство text
, добавьте локальное свойство (например, localText
). Я бы также использовал свойство computed
для вычисления длины вместо события для лучшей читабельности. Итак, что-то вроде этого:
<template>
<div>
<label class = "label" :class = "{ 'label-large' : large }" v-if = "label">
{{ label }} <sup class = "is-required" v-if = "isRequired">Req</sup>
</label>
<input class = "input-control" :class = "{ 'input-large' : large }" :maxlength = "maxLength" :placeholder = "placeholderText" ref = "input" v-model = "localText" @change = "formatValue($event.target.value)" />
<div class = "flex text-x-small-regular mt-2" :class = "large ? 'px-4' : 'px-2'" v-if = "maxLength || validationFailed">
<div class = "validation-message">
<template v-if = "validationFailed">{{ validationMessage }}</template>
</div>
<div class = "character-count" v-if = "maxLength">
<span :class = "characterCountWarningStyle">{{ characterCount }}</span> / {{ maxLength }}
</div>
</div>
</div>
</template>
<script>
export default {
props: {
isRequired: {
default: false,
required: false,
type: Boolean
},
label: {
required: false,
type: String
},
large: {
default: false,
required: false,
type: Boolean,
},
maxLength: {
required: false,
default: 10,
type: Number
},
placeholder: {
required: false,
type: String
},
text: {
required: false,
type: String
},
validationMessage: {
default: "Required field.",
required: false,
type: String
}
},
data() {
return {
localText: this.text || '',
validationFailed: false,
value: undefined
}
},
computed: {
characterCount() {
return this.localText.length;
},
characterCountWarningStyle() {
return "" // Simplified.
},
placeholderText() {
return "" // Simplified.
}
},
methods: {
formatValue(value) {
this.validationFailed = false;
if (value) value = value.trim();
this.validate(value);
},
validate(value) {
if (this.isRequired && !value) {
this.validationFailed = true;
}
this.$emit('update', value);
}
}
}
</script>
Обновление characterCount
в обработчике keyup
запускает повторную визуализацию всего компонента, чтобы отобразить новое значение интерполяции строки characterCount
в шаблоне. Рендеринг включает <input>
, значение которого привязано к text
. Если text
является пустой строкой или нулевым значением, <input>
эффективно очищается на keyup
.
Чтобы решить эту проблему, используйте локальную копию реквизита text
, которую можно изменить, и привяжите ее к <input>
объекта v-model
.
Создайте свойство данных (с именем "value"
) и наблюдатель на реквизите text
, который копирует text
в value
:
export default {
props: {
text: {/*...*/},
},
data() {
return {
value: ''
}
},
watch: {
text(newText) {
this.value = newText
}
},
}
Используйте новое свойство value
в качестве v-модели <input>
:
<input v-model = "value">
Удалите обработчик keyup
и свойство данных characterCount
и вместо этого используйте вычисляемую опору, которая возвращает длину value
:
export default {
computed: {
characterCount() {
return this.value.length
}
},
}
Не могу воспроизвести ошибку. Не могли бы вы обновить эту скрипку, чтобы она продемонстрировала проблему?