<template> <div class="form-control w-full"> <label class="label"> <span class="label-text">{{ label }}</span> </label> <select v-model="selectedIdx" class="select select-bordered"> <option disabled selected>Pick one</option> <option v-for="(obj, idx) in items" :key="name != '' ? obj[name] : obj" :value="idx"> {{ name != "" ? obj[name] : obj }} </option> </select> <!-- <label class="label"> <span class="label-text-alt">Alt label</span> <span class="label-text-alt">Alt label</span> </label> --> </div> </template> <script lang="ts" setup> const emit = defineEmits(["update:modelValue", "update:value"]); const props = defineProps({ label: { type: String, default: "", }, modelValue: { type: [Object, String] as any, default: null, }, items: { type: Array as () => any[], required: true, }, name: { type: String, default: "name", }, valueKey: { type: String, default: null, }, value: { type: String, default: "", }, compareKey: { type: String, default: null, }, }); const selectedIdx = ref(-1); const internalSelected = useVModel(props, "modelValue", emit); const internalValue = useVModel(props, "value", emit); watch( selectedIdx, newVal => { if (newVal === -1) { return; } if (props.value) { internalValue.value = props.items[newVal][props.valueKey]; } internalSelected.value = props.items[newVal]; }, { immediate: true } ); watch( [internalSelected, () => props.value], () => { if (props.valueKey) { const idx = props.items.findIndex(item => compare(item, internalValue.value)); selectedIdx.value = idx; return; } const idx = props.items.findIndex(item => compare(item, internalSelected.value)); selectedIdx.value = idx; }, { immediate: true } ); function compare(a: any, b: any): boolean { if (a === b) { return true; } if (props.valueKey) { return a[props.valueKey] === b; } // Try compare key if (props.compareKey && a && b) { return a[props.compareKey] === b[props.compareKey]; } return JSON.stringify(a) === JSON.stringify(b); } </script>