Vue 3 Как передавать реактивный объект в компонент и обратно
Есть обычный родительский компонент. Который должен передавать в дочерний компонент некий реактивный объект и получать обратно при изменении.
<template>
<section>
<cl-component v-model="data"></cl-component >
</section>
</template>
<script lang="ts">
export default defineComponent({
setup() {
const data= reactive({
HH: "09",
MM: "50",
})
return {
data,
}
},
})
</script>
Дочерний компонент. Который выводит список времени. При нажатии на ячейку времени, срабатывает функция selectTime. Как вернуть эти данные обратно в родителя?
<template>
....
</template>
<script lang="ts">
export default defineComponent({
props: {
modelValue: {
type: Object,
default: () => {
return {
HH: "00",
MM: "00",
}
},
},
},
emits: ["update:modelValue"],
setup(props, context) {
const hour = ref("" as string)
const minute = ref("" as string)
const selectTime = (type: string, value: string) => {
// ...
hour.value = value
// ...
minute.value = value
//context.emit("update:modelValue", {???????})
}
return {
hour,
minute,
}
},
})
</script>
Ответы (1 шт):
В Vue3, вы можете использовать аргумент директивы v-model, это позволит иметь несколько двухсторонних привязок v-model на пользовательском компоненте как альтернатива использования объекта.
В дочернем компоненте можно создать собственные v-model, потому как нельзя изменять значения свойств изprops прямой манипуляцией (связано это с односторонней привязкой), допустим следующего вида:
const hourValue = Vue.computed({
get: () => props.hour,
set: (val) => {
h.value = val
context.emit('update:hour', val < 10 ? `0${val}` : val)
},
})
и после этого достаточно назначить элементу, отвечающему за выбор времени это вычисляемое свойство как v-model,
<input type="range" v-model="hourValue" min="00" max="23" step="1">
Пример использования данного подхода
const app = Vue.createApp({
data: () => ({
time: {
HH: "10",
MM: "00",
}
}),
template: `
<fieldset>
<legend>Родительский компонент</legend>
<pre>{{ $data }}</pre>
<v-timing v-model:hour="time.HH" v-model:minute="time.MM"></v-timing>
</fieldset>
`
});
app.component('v-timing', {
props: {
minute: {
type: String,
default: "00",
},
hour: {
type: String,
default: "00",
},
},
emits: ["update:minute", "update:hour"],
setup(props, context) {
const h = Vue.ref(props.hour)
const m = Vue.ref(props.minute)
const hourValue = Vue.computed({
get: () => props.hour,
set: (val) => {
h.value = val
context.emit('update:hour', val < 10 ? `0${val}` : val)
},
})
const minuteValue = Vue.computed({
get: () => props.minute,
set: (val) => {
m.value = val
context.emit('update:minute', val < 10 ? `0${val}` : val)
},
})
return {
minuteValue,
hourValue,
h,
m,
}
},
template: `
<fieldset>
<legend>Дочерний компонент</legend>
Час = <input type="range" v-model="hourValue" min="00" max="23" step="1">
Минута =
<input type="range" v-model="minuteValue" min="00" max="59" step="5">
<hr>
Выберите час
<div class="flex">
<template v-for="i in 23">
<div :class="['box', { active: i == h}]" @click="hourValue=i.toString()">{{i}}</div>
</template>
</div>
Выберите минуту
<div class="flex">
<template v-for="i in [5,10,15,20,25,30,35,40,50]">
<div :class="['box', { active: i == m}]" @click="minuteValue=i.toString()">{{i}}</div>
</template>
</div>
</fieldset>`
});
app.mount('#app');
.flex {
display: flex;
flex-wrap: wrap;
align-items: center;
}
.box {
padding: 10px;
background: #42b983;
}
.box:hover,
.box.active {
background: #7d7dd7;
color: white;
cursor: pointer;
}
<script src="https://unpkg.com/vue@next"></script>
<div id="app"></div>