In Vue 3, when emitting an event from child to parent to update refs and subsequently refresh the props of the child the form control flow breaks i.e. tabindex or [tab]
ing between fields doesn't work. My code is roughly setup as follows:
Parent.vue
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const connection = ref(null)
const change = (updated) => {
connection.value = updated
}
</script>
<template>
<Child :connection="connection" @update="change" />
</template>
Child.vue
<script setup>
import { ref } from 'vue'
const props = defineProps({
connection: Object,
})
const emit = defineEmits(['update'])
const a = ref(props.connection?.a)
const b = ref(props.connection?.b)
const c = ref(props.connection?.c)
const update = () => {
emit('update', {
a: a.value,
b: b.value,
c: c.value,
})
}
</script>
<template>
<input v-model="a" @blur="update" />
<input v-model="b" @blur="update" />
<input v-model="c" @blur="update" />
</template>
I'm guessing due to it being an object that it breaks the the flow as it re-renders the whole child and loses focus. Question is how can I update the object and keep the focus the same?
Side note: I don't want to emit individual model value events like below because I don't want the parent to have to deal with every property since I'm creating several distinct Child
vues for different forms.
<script setup>
defineProps({
firstName: String,
lastName: String
})
defineEmits(['update:firstName', 'update:lastName'])
</script>
<template>
<input
type="text"
:value="firstName"
@input="$emit('update:firstName', $event.target.value)"
/>
<input
type="text"
:value="lastName"
@input="$emit('update:lastName', $event.target.value)"
/>
</template>
This would lead to a parent like this and I'll end up having to handle a-z
...
<Child
v-model:a="connection.a"
v-model:b="connection.b"
v-model:c="connection.c"
...
Try to simplify the Child component state with a writable computed that get the prop values and emit the new values when it's mutated :
<script setup>
import { computed } from 'vue'
const props = defineProps({
modelValue: Object,
})
const emit = defineEmits(['update:modelValue'])
const model = computed({
get: () => props.modelValue,
set: (val) => {
emit('update:modelValue', val)
}
})
</script>
<template>
<input v-model="model.a" @blur="update" />
<input v-model="model.b" @blur="update" />
<input v-model="model.c" @blur="update" />
</template>
Parent component :
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const connection = ref({})
</script>
<template>
<Child v-model="connection" />
</template>