Exploring the vuetify realm as a newcomer, I find myself grappling with event handling while working on my first web app project. Specifically, I am currently developing a "UserPicker" component using VAutocomplete.
This component functions by sending an asynchronous request to a server that responds with a list of user accounts matching the input provided by the user.
The desired outcome should resemble the example demonstrated here: https://vuetifyjs.com/en/components/autocompletes/#slots
Everything seems to be functioning correctly, except for my difficulty in capturing events related to this specific issue.
Below is the code I have crafted:
<script setup lang="ts">
import { ref, watch } from "vue";
import { useServerRequestsStore } from "@/stores/server-requests";
const input = ref();
const items = ref<Array<UserPreview>>([]);
const select = ref<Array<UserPreview>>([]);
const selectSet = new Set();
const search = ref("");
const serverRequests = useServerRequestsStore();
interface UserPreview {
firstName: string;
lastName: string;
username: string;
avatar: string;
}
function addUser(userItem: UserPreview) {
try {
selectSet.add(userItem.username);
select.value.push(userItem);
items.value = [];
search.value = "";
input.value.focus();
} catch (error) {
console.log(error);
}
}
function removeUser(userItem: UserPreview) {
selectSet.delete(userItem.username);
const toRemove = select.value.findIndex(
(added) => added.username == userItem.username
);
console.log("removing " + userItem.username + " at index " + toRemove);
select.value.splice(toRemove, 1);
console.log("after removal:", select.value);
}
watch(search, async (val) => {
if (val.length == 0) return;
try {
const users = await serverRequests.getUserByUsername(val, false);
items.value = users
.filter((user: any) => !selectSet.has(user.userInfo.username))
.map((user: any) => ({
firstName: user.userInfo.firstName,
lastName: user.userInfo.lastName,
username: user.userInfo.username,
title: user.userInfo.username, //richiesto per far comparire automaticamente i suggerimenti
avatar: user.userInfo.profilePicture,
}));
} catch (error) {
items.value = [];
}
});
</script>
<template>
<VAutocomplete
@keydown.enter.prevent
ref="input"
v-model="select"
v-model:search="search"
:items="items"
class="mx-4"
density="compact"
hide-no-data
hide-details
label="Seleziona destinatari"
style="max-width: 300px"
chips
closable-chips
multiple
>
<template v-slot:item="{ item }"
><VListItem class="text-center" @click="addUser(item.raw)">
<VAvatar size="40px" color="black" variant="outlined"
><VImg alt="profile avatar" :src="item.raw.avatar" />
</VAvatar>
<VListItemTitle
>{{ item.raw.firstName }} {{ item.raw.lastName }}</VListItemTitle
>
<VListItemSubtitle>{{ item.raw.username }}</VListItemSubtitle>
</VListItem>
</template>
<template v-slot:chip="{ item }">
<VChip
@click:close="removeUser(item.raw)"
:text="item.raw.username"
></VChip>
</template>
</VAutocomplete>
</template>
In the bottom segment of the template, where the VChip component is utilized, I am adept at adequately managing the @click:close event. However, in this context, the VChip can also be closed by selecting them using arrow keys and pressing backspace within the VAutocomplete edit mode.
I have experimented with various options such as @vnode-before-unmount, @keydown.backspace, and @update:model-value events, but none have yielded the desired result. An important observation is that @vnode-before-unmount event indeed triggers successfully and calls the removeUser function accurately. However, peculiarly, the scope of the component does not persist through the process. In fact, the select reference is empty when the handler is invoked through this event, whereas it is properly populated when triggered via the @click:close event.
I appreciate your assistance in advance.