Search code examples
vue.jsvuejs3

Vue: proxy framework component with v-model


I want to proxy <vue-date-picker/>, which exposes a Date-based v-model in a way that it can be used with Dayjs (which we use across the app) instead. How can I achieve this? I am on latest Vue with composition API.

I know I need to use @update:model-value and :model-value to split the 2-way data binding into 2x1-way and have a conversion in between, but I don't know how to pass a v-model value into a ref() and at the same time emit events which can be consumed as a v-model in a parent component. I tried out many variations, my current code looks like this:

const convertedValue = ref(dayjs(props.value).toDate());
watchEffect(() => {
  convertedValue.value = dayjs(props.value).toDate();
});
watchEffect(() => {
  emit('update:value', dayjs(convertedValue.value));
});
...
<vue-date-picker :model-value="convertedValue" />

But it just does not work. This should be a pretty common use-case, no? What can I do?


EDIT: thanks to the answer from Alexander and a colleague of mine, I managed to get it working like this:

const value = computed(() => convertToDatePicker(props.modelValue));
const emit = defineEmits<{
  'update:model-value': [date: dayjs.Dayjs | null];
}>();
function handleEmit(event: ModelValue) {
  emit('update:model-value', convertToDayjs(event));
}
...
:model-value="value"
@update:model-value="handleEmit"

Solution

  • There is a standard way to use computed():

    // use it as v-model for the input in the template
    const value = computed({
      get(){
        return dayjs(props.value).toDate();
      },
      set(val){
        emit('update:value', dayjs(val));
      }
      
    });
    

    I also suggest to use modelValue instead of value as the prop so you can bind your proxy component with clean v-model too.

    In the Vue 3.3 a macro defineModel() is available which accepts the same get/set as the second parameter. It is marked as experimental, stable in 3.4