I have a Vuetify autocomplete with multiple selection with chips whose suggestions dropdown are not stored locally, but are always retrieved asynchronously from the server when the user starts to type in the input field. The code looks something like this:
fetch("/echo/html/", {
method: "POST",
headers: {
"Content-Type": "application/json; charset=utf-8"
},
body: "html=<a href='#'>hello world</a>"
})
.then((response) => response.rows = [{
id: "6rfsda",
name: "Cristiano Ronaldo",
club: "Juventus FC"
},
{
id: "vfgfas",
name: "Lionel Messi",
club: "FC Barcelona"
},
{
id: "fgfaex",
name: "Mohammd Salah",
club: "Liverpool FC"
}
])
.catch((error) => {
console.error(error)
})
var vm = new Vue({
el: "#app",
data() {
return {
selectedFootballPlayers: [],
footballPlayers: [],
footballPlayerSearch: {
loading: false,
search: ''
}
}
},
watch: {
'footballPlayer.search': function watch(val) {
this.queryFootballPlayers(val);
},
selectedFootballPlayers() {
this.footballPlayerSearch.search = '';
},
},
methods: {
async queryFootballPlayers(val) {
this.footballPlayerSearch.loading = true;
this.footballPlayers = [];
if (this.selectedFootballPlayers.length > 0) {
this.selectedFootballPlayers.forEach((player) => this.footballPlayers.push(player));
}
let response;
try {
const result = apiUrl.findFootballPlayers({
query: val
}); //I tried to get the async simulation call to work, but I have no clue how
response = await result.response;
} catch (err) {
console.log('There was an error');
}
this.footballPlayerSearch.loading = false;
},
remove(item) {
const itemIndex = this.selectedFootballPlayers.findIndex((el) => el.id === item.id);
this.selectedFootballPlayers.splice(itemIndex, 1);
},
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet">
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet" type="text/css">
<div id="app">
<v-app light>
<v-autocomplete v-model="selectedFootballPlayers" :items="footballPlayers" label="Favorite Footbal Players" :loading="footballPlayerSearch.loading" :no-data-text="'No football players were found'" :search-input.sync="footballPlayerSearch.search" item-text="name"
return-object no-filter multiple style="width: 100%">
<template v-slot:selection="data">
<v-chip
v-bind="data.attrs"
:input-value="data.selected"
>
<span>{{ data.item.name }}</span>
<v-icon
right
size="18"
@click="remove(data.item)"
>
close
</v-icon>
</v-chip>
</template>
<template slot="item" slot-scope="{ item }">
<v-list-tile-content>
<div class="d-flex align-center justify-center">
<v-checkbox
v-model="item.selected"
:ripple="false"
:label="item.name"
multiple
/>
{{ item.name }}
</div>
</v-list-tile-content>
</template>
</v-autocomplete>
</v-app>
</div>
(Unfortunately, I could not get the code to run in JSFiddle because I don't know how to mock async calls there.)
The flow is as follows:
- User focuses on the v-autocomplete field. A first call to the server is made for the first 20 football players.
- The user starts to type a string. A new call is made to the server with the string as parameter and new results are returned.
- The user selects some of the football players from the result.
- The user types some new string, and so on.
What happens now? When the user types a new search, the chips from the previous selection disappear immediately. If he selects a chip from the new list of suggestions, the chip is shown in the input, until a new search is made, and then the chip disappears. This is explainable by the fact that the chips are not included in the suggestions. I found a hack around it, which is to always push the pre-existing selection in the suggestions, but it's ugly and it's not what a user would expect to happen.
I considered using a combobox instead, but I'm not sure if that would solve my issue.