custom-select.vue
<template>
<div class="relative">
<label
:for="$attrs.name"
class="block text-sm font-medium leading-6 text-gray-900"
>{{ $t($attrs.label) }} {{ required ? '*' : '' }}</label
>
<i
v-if="!disabled"
class="fa-light absolute right-3 top-11 fa-chevron-down"
></i>
<select
@input="changed"
:value="value"
:disabled="disabled"
:class="errorInputStyle"
v-bind="$attrs"
class="mt-2 appearance-none disabled:cursor-not-allowed disabled:bg-gray-100 outline-none disabled:text-gray-600 disabled:ring-gray-200 bg-white block shadow w-full rounded-md border-0 py-2 pl-3 pr-10 ring-1 ring-inset focus:ring-2 sm:text-sm sm:leading-6"
>
<option :selected="value === defaultValue" :value="defaultValue" disabled>
{{ $t($attrs.placeholder) || 'please select' }}
</option>
<option
:selected="option.value === value"
:value="option.value"
v-for="option in options"
>
{{ $t(option.text) }}
</option>
</select>
<p :class="errorDescriptionStyle" class="mt-2 text-sm">
{{ $t($attrs.description) }}
</p>
</div>
</template>
<script lang="ts">
import { computed, onMounted, ref } from 'vue'
import { BaseOptionT } from '~/types'
export default {
props: {
value: {
default: '',
},
options: {
default: () => [] as BaseOptionT[],
},
required: {
default: false,
},
disabled: {
default: false,
},
isError: {
default: false,
},
},
setup(props, { emit }) {
let selectValue = ref('')
let defaultValue = ref()
function changed(e: any) {
let target = e.target as HTMLSelectElement
try {
return emit('input', JSON.parse(target.value))
} catch (err) {
return emit('input', target.value)
}
}
let errorDescriptionStyle = computed(() => [
props.isError ? 'text-red-500' : 'text-gray-500',
])
onMounted(() => {
if (!props.options.some((o) => o.value === props.value)) {
defaultValue.value = props.value
}
})
let errorInputStyle = computed(() => [
props.isError
? 'text-red-900 ring-red-300 placeholder:text-red-300 focus:ring-red-600'
: 'text-gray-900 ring-gray-300 placeholder:text-gray-300 focus:ring-green-600',
])
return {
changed,
defaultValue,
selectValue,
errorDescriptionStyle,
errorInputStyle,
}
},
}
</script>
Here is the situation:
<custom-select v-model="group.billing_waiting_period" :options="billWaitingPeriods" />
Whenever I use my component like this, and in this specific case billing_waiting_period
Exists but contains a value of null
, Vue fails to update the user interface when I choose something different from the dropdown. The group
object is a prop
Here is additional context:
let billWaitingPeriods = computed(() =>
Object.values(BillWaitingPeriod).map((interval) => {
switch (interval) {
case BillWaitingPeriod.ONE_MONTH:
return { value: interval, text: '1 Month' }
case BillWaitingPeriod.TWO_MONTH:
return { value: interval, text: '2 Months' }
case BillWaitingPeriod.THREE_MONTH:
return { value: interval, text: '3 Months' }
default:
return { value: interval, text: '1 Month' }
}
})
)
export enum BillWaitingPeriod {
ONE_MONTH = "ONE_MONTH",
TWO_MONTH = "TWO_MONTH",
THREE_MONTH = "THREE_MONTH"
}