自定义自动完成输入清除所选选项。 `更新:modelValue`问题
我正在尝试使用 debo 创建自动完成输入,但由于某种原因我无法让它正常工作。 我可以从下拉菜单中选择一个建议,并将其短暂显示在屏幕上,但随后它会与输入本身一起重置。
我很确定错误来自 selectSuggestion
函数的最后一行清除 valueInner
,但在选择值后清除输入正是我想要的。但是,我不希望在发生这种情况后清除选定的值。
它可以是 selectSuggestion
函数和 valueInner
上的 watch
的组合,但我还需要 watch
来使去抖工作。
关于如何解决这个问题有什么想法吗?
CodeSandbox 链接: https ://codesandbox.io/s/stoic-forest-7w19oj?file=/src/components/Autocomplete.vue
App.vue
<template>
<Autocomplete v-model="text" :suggestions="['something','hairstyle','cheese']"/>
<p>Selected Suggestion:{{text}}</p>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import Autocomplete from '../src/components/Autocomplete'
export default defineComponent({
name: "App",
setup(){
const text = ref('')
return { text }
},
components: {
Autocomplete,
Autocomplete2,
},
});
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
自动完成
<template>
<input
ref="inputRef"
v-model="valueInner"
:disabled="disabled || undefined"
:placeholder="placeholder"
:readonly="readonly || undefined"
:required="required || undefined"
:autofocus="autofocus"
:autocomplete="autocomplete"
:suggestions="suggestions"
/>
<ul v-if="filterSuggestions.length">
<li>
Showing {{ filterSuggestions.length }} of {{ suggestions.length }} results
</li>
<li
v-for="suggestion in filterSuggestions"
:key="suggestion"
@click="selectSuggestion(suggestion)"
>
{{ suggestion}}
</li>
</ul>
</template>
<script lang="ts">
import {
defineComponent,
ref,
onMounted,
nextTick,
watch,
PropType,
computed,
} from 'vue'
export default defineComponent({
name: 'Autocomplete',
inheritAttrs: false,
props: {
label: {
type: String,
},
disabled: { type: Boolean, default: false },
placeholder: { type: String },
readonly: { type: Boolean },
required: { type: Boolean },
modelValue: { type: [String, Number], default: '' },
autocomplete: { type: Boolean, default: false },
suggestions: {
type: Array as PropType<string[]>,
default: ['Hamburger', 'Fries', 'Peanuts'],
},
debounce: { type: Number, default: undefined },
},
emits: ['update:modelValue'],
setup(props, { emit }) {
let debounceInner = 0
if (typeof props.debounce === 'number') {
debounceInner = props.debounce
} debounceInner = 1000
const inputRef = ref<HTMLElement | null>(null)
const isFocused = ref(false)
watch(isFocused, (oldVal, newVal) => {
console.log(`isFocused → `, isFocused)
})
let timeout: any = null
const valueInner = ref<any>(props.modelValue)
console.log(valueInner.value)
watch(valueInner, (newVal, oldVal) => {
const debounceMs = debounceInner
if (debounceMs > 0) {
clearTimeout(timeout)
timeout = setTimeout(() => emitInput(newVal), debounceMs)
} else {
emitInput(newVal)
}
})
function emitInput(newVal: any) {
let payload = newVal
emit('update:modelValue', payload)
}
let selectedSuggestion = ref('')
const suggestions = ['United States', 'Japan', 'Italy', 'Australia', 'Germany', 'Poland', 'Ukraine']
const filterSuggestions = computed(() => {
if (valueInner.value === '') {
return []
}
let matches = 0
return suggestions.filter((suggestion) =>{
if (
suggestion.toLowerCase().includes(valueInner.value.toLowerCase())
&& matches < 10
) {
matches++
return suggestion
}
})
})
const selectSuggestion = (suggestion) => {
selectedSuggestion.value = suggestion
emitInput(suggestion)
valueInner.value = ''
}
function emitInput(newVal: any) {
let payload = newVal
emit('update:modelValue', payload)
}
watch(valueInner, (newVal, oldVal) => {
const debounceMs = debounceInner
if (debounceMs > 0) {
clearTimeout(timeout)
timeout = setTimeout(() => emitInput(newVal), debounceMs)
} else {
emitInput(newVal)
}
})
return {
inputRef,
valueInner,
suggestions,
filterSuggestions,
selectSuggestion,
selectedSuggestion,
}
},
})
</script>
<style lang="sass" scoped>
li
list-style: none
</style>
I'm tyring to create a autocomplete input with deboand for some reason I am not able to get it to work properly.
I am able to select a suggestion from the drop down and show it briefly on the screen, but then it resets alongside the input itself.
I'm pretty sure the error comes from the selectSuggestion
function's last line to clear the valueInner
, but clearing the input once the value is selected is what I want. I don't want, however, the selected value to clear after that has happened.
It could be a combination of the selectSuggestion
function and the watch
on valueInner
, but I also need that watch
to make the debounce work.
Any ideas on how I can fix this?
CodeSandbox Link: https://codesandbox.io/s/stoic-forest-7w19oj?file=/src/components/Autocomplete.vue
App.vue
<template>
<Autocomplete v-model="text" :suggestions="['something','hairstyle','cheese']"/>
<p>Selected Suggestion:{{text}}</p>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import Autocomplete from '../src/components/Autocomplete'
export default defineComponent({
name: "App",
setup(){
const text = ref('')
return { text }
},
components: {
Autocomplete,
Autocomplete2,
},
});
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Autocomplete
<template>
<input
ref="inputRef"
v-model="valueInner"
:disabled="disabled || undefined"
:placeholder="placeholder"
:readonly="readonly || undefined"
:required="required || undefined"
:autofocus="autofocus"
:autocomplete="autocomplete"
:suggestions="suggestions"
/>
<ul v-if="filterSuggestions.length">
<li>
Showing {{ filterSuggestions.length }} of {{ suggestions.length }} results
</li>
<li
v-for="suggestion in filterSuggestions"
:key="suggestion"
@click="selectSuggestion(suggestion)"
>
{{ suggestion}}
</li>
</ul>
</template>
<script lang="ts">
import {
defineComponent,
ref,
onMounted,
nextTick,
watch,
PropType,
computed,
} from 'vue'
export default defineComponent({
name: 'Autocomplete',
inheritAttrs: false,
props: {
label: {
type: String,
},
disabled: { type: Boolean, default: false },
placeholder: { type: String },
readonly: { type: Boolean },
required: { type: Boolean },
modelValue: { type: [String, Number], default: '' },
autocomplete: { type: Boolean, default: false },
suggestions: {
type: Array as PropType<string[]>,
default: ['Hamburger', 'Fries', 'Peanuts'],
},
debounce: { type: Number, default: undefined },
},
emits: ['update:modelValue'],
setup(props, { emit }) {
let debounceInner = 0
if (typeof props.debounce === 'number') {
debounceInner = props.debounce
} debounceInner = 1000
const inputRef = ref<HTMLElement | null>(null)
const isFocused = ref(false)
watch(isFocused, (oldVal, newVal) => {
console.log(`isFocused → `, isFocused)
})
let timeout: any = null
const valueInner = ref<any>(props.modelValue)
console.log(valueInner.value)
watch(valueInner, (newVal, oldVal) => {
const debounceMs = debounceInner
if (debounceMs > 0) {
clearTimeout(timeout)
timeout = setTimeout(() => emitInput(newVal), debounceMs)
} else {
emitInput(newVal)
}
})
function emitInput(newVal: any) {
let payload = newVal
emit('update:modelValue', payload)
}
let selectedSuggestion = ref('')
const suggestions = ['United States', 'Japan', 'Italy', 'Australia', 'Germany', 'Poland', 'Ukraine']
const filterSuggestions = computed(() => {
if (valueInner.value === '') {
return []
}
let matches = 0
return suggestions.filter((suggestion) =>{
if (
suggestion.toLowerCase().includes(valueInner.value.toLowerCase())
&& matches < 10
) {
matches++
return suggestion
}
})
})
const selectSuggestion = (suggestion) => {
selectedSuggestion.value = suggestion
emitInput(suggestion)
valueInner.value = ''
}
function emitInput(newVal: any) {
let payload = newVal
emit('update:modelValue', payload)
}
watch(valueInner, (newVal, oldVal) => {
const debounceMs = debounceInner
if (debounceMs > 0) {
clearTimeout(timeout)
timeout = setTimeout(() => emitInput(newVal), debounceMs)
} else {
emitInput(newVal)
}
})
return {
inputRef,
valueInner,
suggestions,
filterSuggestions,
selectSuggestion,
selectedSuggestion,
}
},
})
</script>
<style lang="sass" scoped>
li
list-style: none
</style>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
该错误确实来自于清除
valueInner.value
,这会触发其上的监视,从而将空白值作为update:modelValue
事件数据发出,从而覆盖模型值。快速解决方法是在重置
valueInner.value
时使用null
而不是空字符串(以区分内部清除与用户输入):The error is indeed from clearing
valueInner.value
, which triggers the watch on it, which emits the blank value as theupdate:modelValue
event data, which overwrites the model value.A quick fix is to use
null
instead of an empty string (to distinguish between an internal clear versus user input) when resettingvalueInner.value
:Then in the watch, return immediately if the new value is
null
because we don't need to emit that:Also, in
filterSuggestions
, return an empty array when the value is falsy (includesnull
or empty string):demo