You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vuetify v-autocomplete选中项与用户点击项不符问题

问题解决:v-autocomplete选中选项后显示错误内容

问题现象

开发基于BoardGameGeeks API的v-autocomplete组件时,为区分同名项给选项追加了出版年份(如“Crossbows and Catapults (1983)”),但点击“Crossbows and Catapults (1983)”选项后,输入字段却显示“Battleground: Crossbows & Catapults (2007)”。

问题原因

  1. DOM元素作为v-model值导致匹配失效:当前selectedItem绑定的是XML解析后的DOM元素,Vuetify的v-autocomplete依赖对象引用一致性匹配选中值与选项。后续搜索更新items时,新生成的DOM元素和之前选中的不是同一引用,组件无法正确匹配,从而显示错误文本。
  2. 缺失选中项状态跟踪:代码中使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 01:52:33