英文:
Why isn't reactive the data with props in this situation?
问题
我在`Desks.vue`组件中有两个组件,`Rooms.vue`和`Desks.vue`,除了`App.vue`组件。
当我在`Rooms.vue`组件中点击一个滑动元素时,会打开`Desks.vue`组件并将`room`对象传递到`App.vue`,然后传递到`Desks.vue`作为属性。在`Desks.vue`中,属性`room`是响应式的,所以当我点击另一个滑动元素时,`prop.room`会发生变化。然而,在`reserveData`中,`room`属性没有变化。
你能告诉我为什么在`reserveData`对象中`room`属性不是响应式的吗?
英文:
I have two componets beside the App.vue
component, the Rooms.vue
and the Desks.vue
.
When I click on the on one of the swiper element in the Rooms.vue
component, then opens the Desks.vue component and emits the room
object to App.vue
, and from there is passed down into the Desks.vue
as prop. In the Desks.vue
the prop room
is reactive, so when I click on another swiper element, the prop.room
is changed. However, in the reserveData
the room
property doesn't change.
Can you tell me why isn't reactive the room
prop in the reserveData
object?
App.vue
const room = ref(null)
const openRoom = (rm) => {
room.value = rm
console.log(room.value)
}
const closeRoom = () => {
room.value = null
}
</script>
<template>
<Header title="Friendbook"/>
<Rooms @openRoom="openRoom" :room="room"/>
<transition>
<Desks v-if="room" :room="room" @closeRoom="closeRoom"/>
</transition>
</template>
Rooms.vue
const emit = defineEmits(['openRoom'])
const props = defineProps({room: Object})
const roomStore = useRoomStore()
const onSwiper = (swiper) => {
// console.log(swiper)
};
const onSlideChange = () => {
// console.log('slide change');
};
const activeRoomIdx = ref(null)
const clickOnRoom = (room, idx) => {
activeRoomIdx.value = idx;
emit('openRoom', room);
}
watch(() => props.room, (newVal, oldVal) => {
if (!newVal) {
activeRoomIdx.value = null
}
})
</script>
<template>
<div class="container">
<h3>Rooms</h3>
<div class="rooms-container">
<span class="swiper-button prev"></span>
<swiper
:modules="swiperOptions.modules"
:space-between="12"
@swiper="onSwiper"
@slideChange="onSlideChange"
:navigation="swiperOptions.navigation"
:breakpoints="swiperOptions.breakpoints"
>
<swiper-slide v-for="(room, idx) in roomStore.rooms">
<div class="room-container" :class="{active: activeRoomIdx === idx}" @click="clickOnRoom(room, idx)">
{{ room.name }}
</div>
</swiper-slide>
</swiper>
<span class="swiper-button next"></span>
</div>
</div>
</template>
Desks.vue
const props = defineProps({
room: Object,
})
const {room} = toRefs(props)
defineEmits(['closeRoom'])
const employeeStore = useEmployeeStore()
const roomStore = useRoomStore()
const reserveData = reactive({
employee: false,
date: false,
room: room.value.name,
chosenDesk: false
})
const attrs = ref([])
const onDateChange = (range) => {
console.log(range)
console.log('hello')
}
const range = ref({start: new Date(new Date().getTime() -86400000), end: new Date(new Date().getTime() -86400000)})
const error = ref(false)
const makeDate = (date) => {
let day = date.getUTCDate();
let month = date.getUTCMonth() + 1;
let year = date.getUTCFullYear();
if (day < 10) {
day = `0${day}`
}
if (month < 10) {
month = `0${month}`
}
return `${day}-${month}-${year}`
}
const choseDesk = (newDesk) => {
reserveData.chosenDesk = newDesk.name
const desks = roomStore.rooms.find(room => room.name === props.room.name).desks
const currDeskReserves = desks.find(desk => desk.name === newDesk.name).reserved
attrs.value = currDeskReserves.map(cdr => {
return {
dot: 'red',
popover: {
label: cdr.person,
visibility: 'hover',
hideIndicator: false,
},
...cdr
}
})
}
watch(() => range.value, (newVal, oldVal) => {
reserveData.date = newVal
})
</script>
<template>
<div class="container">
<h3>{{ room.name }} Desks</h3>
<div class="container-wrapper">
<div class="desks-container">
<div class="desk" :class="{active: reserveData.chosenDesk === desk.name}" @click="choseDesk(desk)"
v-for="desk in room.desks">
{{ desk.name }}
</div>
</div>
<div class="inputs-container">
<select name="employees" id="employees" @change="(e) => reserveData.employee = e.target.value">
<option value="" selected disabled hidden>Für:</option>
<option v-for="employee in employeeStore.employees" :key="employee" :value="employee">{{ employee }}</option>
</select>
<div class="from-to-container">
<label for=""><span>Von:</span><input :value="makeDate(range.start)" type="text" id="from" disabled></label>
<label for=""><span>Bis:</span><input :value="makeDate(range.end)" type="text" id="to" disabled></label>
</div>
<div class="buttons-container">
<div v-if="error" class="error-message">Some days are reserved</div>
<div class="buttons">
<input class="small" type="button" value="Buchen" @click="roomStore.addNewReserve(reserveData)">
<input v-if="error" class="small secondary" type="button" value="Abbrechen">
</div>
</div>
</div>
<div class="calendar-container">
<VDatePicker
@input="onDateChange"
v-model.range="range"
locale="de"
trim-weeks
:min-date="new Date()"
color="indigo"
:first-day-of-week="2"
:attributes="attrs"
/>
</div>
<div class="close-button" @click="$emit('closeRoom')"></div>
</div>
</div>
</template>
答案1
得分: 3
When you initialize reserveData
, you set the room
property to the name of your current room
ref:
const reserveData = reactive({
room: room.value.name,
...
})
This is a value, not the reactive object. So the room
prop is now a string, and it will not change when your room
ref changes.
A simple way to fix it is to put the ref into reserveData
and extract the room name from there, i.e. reserveData.room.name
. Note that in Vue 3.3, you have to use toRef()
with a function if you use it on props:
const room = toRef(() => props.room) // in Vue 3.3
const reserveData = reactive({
room: room,
...
})
A better solution is probably to use a watcher that updates reserveData
when the room changes:
watch(room, () => reserveData.room = room.value.name)
英文:
When you initialize reserveData
, you set the room
property to the name of your current room
ref:
const reserveData = reactive({
room: room.value.name,
...
})
This is a value, not the reactive object. So the room
prop is now a string, and it will not change when your room
ref changes.
A simple way to fix it is to put the ref into reserverData
and extract the room name from there, i.e. reserveData.room.name
. Note that in Vue 3.3, you have to use toRef()
with a function if you use it on props:
const room = toRef(() => props.room) // in Vue 3.3
const reserveData = reactive({
room: room,
...
})
A better solution is probably to use a watcher that updates reserveData
when the room changes:
watch(room, () => reserveData.room = room.value.name)
答案2
得分: 1
你需要将reserveData
对象定义为计算属性。
reserveData
对象中的room
属性不具有响应性,因为它仅在组件渲染时定义一次。如果你希望保持其中一个实际的值从一个prop中获取,那么reserveData
对象必须定义为计算属性:
const reserveData = computed(() => ({
employee: false,
date: false,
room: room.value.name,
chosenDesk: false
}))
另外,你也可以不将reserveData
定义为计算属性,而是创建一个watch函数来跟踪room
prop的变化,并在reserveData
对象中设置新的room
值。
英文:
You have to make your reserveData
object as a computed value.
The room
property of reserveData
object is not reactive because it's defined only once when component was rendered. If you want to keep there an actual value from a prop, the reserveData
object must be computed
const reserveData = computed(() => ({
employee: false,
date: false,
room: room.value.name,
chosenDesk: false
}))
Also, instead of making reserveData
computed, you can make a watch function that will track the prop room
change and set a new value for room
in the reserveData
object.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论