Vue: присоединение события @keyup/@input к свойству данных прерывает ввод формы

Я пытаюсь прикрепить простой счетчик символов к элементу ввода, но как только я показываю его обратно пользователю, ввод прерывается, и я не могу ввести дополнительные символы в поле ввода.

<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>

Подводя итог приведенному выше коду, я делаю некоторую базовую очистку при изменении и хочу запустить подсчет символов при нажатии клавиши. Что мне не хватает?

Не могу воспроизвести ошибку. Не могли бы вы обновить эту скрипку, чтобы она продемонстрировала проблему?

Daniel_Knights 20.12.2020 00:40

Это так странно. Как только я удаляю :value = "text" из HTML, он работает нормально, но как только я добавляю привязку, он начинает блокировать весь ввод. Кстати, я использую Vue 2.6.

David Flink 20.12.2020 00:53

На самом деле попробуйте здесь: jsfiddle.net/r46g3nt1

David Flink 20.12.2020 01:13

Вы мутируете реквизит. Вот с этого надо начинать..

bigless 20.12.2020 01:46

Извините как? Я тоже пробовал это с вычисляемым сеттером. Та же проблема.

David Flink 20.12.2020 02:48

Я не говорил, что это решает вашу проблему. Просто ошибка. Вы можете решить свою проблему, заменив :value на v-model.

bigless 20.12.2020 11:21
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
6
446
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вам нужно заменить :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.

  1. Создайте свойство данных (с именем "value") и наблюдатель на реквизите text, который копирует text в value:

    export default {
      props: {
        text: {/*...*/},
      },
      data() {
        return {
          value: ''
        }
      },
      watch: {
        text(newText) {
          this.value = newText
        }
      },
    }
    
  2. Используйте новое свойство value в качестве v-модели <input> :

    <input v-model = "value">
    
  3. Удалите обработчик keyup и свойство данных characterCount и вместо этого используйте вычисляемую опору, которая возвращает длину value:

    export default {
      computed: {
        characterCount() {
          return this.value.length
        }
      },
    }
    

Другие вопросы по теме