Vuetify v-autocomplete选中项与用户点击项不符问题
问题解决:v-autocomplete选中选项后显示错误内容
问题现象
开发基于BoardGameGeeks API的v-autocomplete组件时,为区分同名项给选项追加了出版年份(如“Crossbows and Catapults (1983)”),但点击“Crossbows and Catapults (1983)”选项后,输入字段却显示“Battleground: Crossbows & Catapults (2007)”。
问题原因
- DOM元素作为v-model值导致匹配失效:当前
selectedItem绑定的是XML解析后的DOM元素,Vuetify的v-autocomplete依赖对象引用一致性匹配选中值与选项。后续搜索更新items时,新生成的DOM元素和之前选中的不是同一引用,组件无法正确匹配,从而显示错误文本。 - 缺失选中项状态跟踪:代码中使用
this.selectedItemDisplayName但未在data中定义,导致this.search !== this.selectedItemDisplayName始终为true,每次输入(包括选择后)都会重新请求并覆盖items,进一步加剧匹配混乱。
修复方案
修复后代码
HTML部分
<div id="app"> <v-app id="inspire"> <v-card> <v-container fluid> <v-row align="center"> <v-col cols="12"> <v-autocomplete :items='items' :search-input.sync='search' v-model="selectedItem" @change="handleItemSelect" ></v-autocomplete> </v-col> </v-row> </v-container> </v-card> </v-app> </div>
JS部分
function bggId(itemElement) { return itemElement.getAttribute('id'); } function primaryName(itemElement) { let retval = null; Array.prototype.slice.call(itemElement.getElementsByTagName("name")).forEach(nameElement => { if(retval === null || nameElement.getAttribute('type') === 'primary') { retval = nameElement.getAttribute('value'); } }) return retval; } function yearPublished(itemElement) { const yearpublishedElements = itemElement.getElementsByTagName("yearpublished"); return (yearpublishedElements.length > 0) ? yearpublishedElements[0].getAttribute("value") : null; } function displayName(itemElement) { const _primaryName = primaryName(itemElement); const _yearPublished = yearPublished(itemElement); return _yearPublished === null ? _primaryName : `${_primaryName} (${_yearPublished})` } function createItemWithPrimaryName(primaryName) { const element = new window.DOMParser().parseFromString("<item type='boardgame'><name type='primary' value='" + primaryName + "'/></item>", 'text/xml').documentElement; element.setAttribute('id', `custom-${Date.now()}`); return element; } async function searchBgg(searchInput) { return await fetch(`https://boardgamegeek.com/xmlapi2/search?type=boardgame,boardgameaccessory,boardgameexpansion&query=${encodeURIComponent(searchInput.replace(/\s/g, '+'))}`) .then(response => response.text()) .then(text => new window.DOMParser().parseFromString(text, 'text/xml')) .then(xml => Array.prototype.slice.call(xml.documentElement.getElementsByTagName('item'))) } new Vue({ el: '#app', vuetify: new Vuetify(), data: () => ({ search: '', items: [], selectedItem: null, selectedItemDisplayName: '' }), methods: { handleItemSelect(item) { this.selectedItemDisplayName = item.text; this.$emit('itemSelected', item.value.element); } }, watch: { async search(localSearch) { if (!this.search) { this.items = []; return; } if (this.selectedItemDisplayName === localSearch) { return; } const localItems = await searchBgg(localSearch).then(items => items.concat([createItemWithPrimaryName(localSearch)])); if (this.search === localSearch) { this.items = localItems.map(item => ({ text: displayName(item), value: { id: bggId(item), element: item } })); } } } })
关键修复说明
- 改用唯一标识作为选项值:将选项
value改为包含BGG ID的对象,确保选中值的唯一性,Vuetify能准确匹配选中项与选项列表。 - 新增选中状态跟踪:添加
selectedItemDisplayName记录当前选中项的显示文本,避免选中后触发不必要的搜索请求。 - 优化搜索触发逻辑:仅当搜索内容与选中项显示名称不一致时,才重新请求数据,防止选项列表被意外覆盖。
内容的提问来源于stack exchange,提问作者spierepf
相关产品推荐
相关产品推荐

