I wrote myself a little helper for the vue 3 v-model binding. Strangely it does not work fully reactive. Internal changes get emitted in a correct way, external changes are not recognised. But if I use the same computed function within my component, everything works as expected. How do I write the helper, to be fully reactive?
The helper:
import { computed, SetupContext, WritableComputedRef } from 'vue';
export const vModel = <T>(val: T, context: SetupContext, binding = 'modelValue'): WritableComputedRef<T> =>
computed({
get: () => val,
set: (value) => {
context.emit(`update:${binding}`, value);
},
});
The sfc:
<template>
<div class="ms-deleteInput">
<input class="ms-deleteInput__input" :label="label" v-model="inputVal" />
<button @click="$emit('delete')" />
</div>
</template>
<script lang="ts">
import { defineComponent, computed } from 'vue';
export default defineComponent({
name: "deleteInput",
props: {
modelValue: {
type: String,
required: true,
},
label: {
type: String,
required: true,
},
},
setup(props, context) {
// This works
const inputVal = computed({
get: () => props.modelValue,
set: (value) => {
context.emit(`update:modelValue`, value);
},
});
// This works, but external changes of modelValue prop will not be detected:
const inputVal = vModel(props.modelValue, context);
return {
inputVal,
};
},
});
</script>
If you do not pass props to the helper, it won't track the props change in computed property.
Pass props instead of props.modelValue to the helper:
const inputVal = vModel(props, context);
Update the helper:
export const vModel = (props, binding) =>
computed({
get: () => props.modelValue,
set: (value) => {
context.emit(`update:${binding}`, value);
},
});
Thank you for your answers! It works now with this:
import { computed, SetupContext, WritableComputedRef } from 'vue';
export const vModel = <T>(props: Record<string, T>, context: SetupContext, binding = 'modelValue'): WritableComputedRef<T> =>
computed({
get: () => props[binding],
set: (value) => {
context.emit(`update:${binding}`, value);
},
});
but the typing ist not correct. I get the error:
Argument of type 'Readonly<LooseRequired<Readonly<{ modelValue?: unknown; label?: unknown; } & { modelValue: string; label: string; } & {}> & { [x: string & `on${string}`]: undefined; }>>' is not assignable to parameter of type 'Record<string, string>'.
'string & `on${string}`' and 'string' index signatures are incompatible.
Type 'undefined' is not assignable to type 'string'.
I tried severall typings for the props
within the function, but they all don't seem to be correct.
I could't find a good way of typing within the vue documentation.