I've got an Inertia app where an event emitted by a child component (which manages a Details/Summary disclosure element) does not reliably update both a Vue ref or supposedly reactive Inertia props. Sometimes the Vue ref will update in Vue Devtools if I click Refresh. The Inertia props do not.
When the disclosure triangle in the child component is clicked, it emits a 'project-toggle' event to the parent with a data object. Opening the disclosure should both add an item to the isExpanded array and change the props.data.projects.expanded property to true. Closing the disclosure does the reverse.
When the disclosure is expanded, the event gets sent correctly, and the handleProjectToggle function logs the changed item to the console, with the expanded property equaling true:
{"id":5,"project_name":"Towards a Sustainable Agriculture - A Curriculum for High School Classes","status_id":3,"description":"The Center for Integrated Agricultural Systems has developed a curriculum on sustainable agriculture for use in high school classes. The curriculum focuses on the importance of citizen and consumer choices as well as agricultural practices that can help the environment and communities along with improving farm profits. The curriculum provides case studies and classroom activities, as well as background information and additional resources for teachers to use.","website":"https://www.cias.wisc.edu/curriculum-new/","school":{"id":1,"school_name":null,"display_name":"College of Agricultural and Life Sciences"},"leader":{"id":7,"first_name":"Diane","last_name":"Mayerfeld","name":"Diane Mayerfeld"},"expanded":true}
But the same item in props.projects.data remains unchanged in Vue Devtools, even when refreshed.
projects:Object
data:Array[50]
0:Object
description:"The Center for Integrated Agricultural Systems has developed a curriculum on sustainable agriculture for use in high school classes. The curriculum focuses on the importance of citizen and consumer choices as well as agricultural practices that can help the environment and communities along with improving farm profits. The curriculum provides case studies and classroom activities, as well as background information and additional resources for teachers to use."
expanded:false
id:5
leader:Object
project_name:"Towards a Sustainable Agriculture - A Curriculum for High School Classes"
school:Object
status_id:3
website:"https://www.cias.wisc.edu/curriculum-new/"
Why are these values not the same?
Parent component:
<script setup>
import { computed, onMounted, ref } from 'vue'
import { usePage } from '@inertiajs/vue3'
const { props } = usePage()
const projects = computed(() => props.projects)
const isExpanded = ref([])
const handleProjectToggle = (obj) => {
props.projects.data?.map(item => {
if (item.id === obj.id) {
if (obj.open) {
item.expanded = true
isExpanded.value.push(item.id)
console.log(JSON.stringify(item))
}
item.expanded = false
isExpanded.value.filter(item => item.id !== obj.id)
}
})
}
</script>
<template>
<ul>
<li
v-for="(project, index) of props.projects.data"
:id="`result-${index}`"
:key="`${index}-${project.id}`"
>
<ProjectResultsDetailsNew
@project-toggle="handleProjectToggle"
:index="index"
:project="project"
/>
</li>
</ul>
</template>
Child component:
<script setup>
import { defineProps, defineEmits, watch, computed, ref } from 'vue'
const props = defineProps( {
project: {
type: Object
},
})
const expanded = computed(() => props.project.expanded)
const open = ref(false)
watch(expanded,(newValue, oldValue) =>
newValue
? open.value = true
: !open.value
)
const emit = defineEmits(
['project-toggle']
)
// default toggle event behavior is prevented and replaced by $emit for parent to handle
const handleToggle = (e) => {
emit('project-toggle', {
open: e.target.open,
index: props.index,
id: props.project.id}
)
open.value = !open.value
}
const openStatus = () => open.value
</script>
<template>
<template>
<details ref="details" :open="open" :class="type" @toggle.prevent="handleToggle($event)">
<summary :id="`project-name-${project.id}`" class="project-name" >
<slot name="summary">
{{ project.project_name }}
</slot>
</summary>
<div class="project-meta">
<div class="meta leader">
<slot name="leaderName">
{{ project.leader.name }}
</slot>
</div>
<div :id="`school-name-${project.id}`" class="meta">
<slot name="school">
{{ project.school.display_name }}
</slot>
</div>
<slot name="profileLink">
<a class="meta full-profile" :href="route('project.show', {project: project.id})" :aria-describedby="`project-name-${project.id}`">
View profile</a>
</slot>
</div>
<div class="details-content">
<h3>Description</h3>
<div class="widb-result-description">
<slot name="description">
{{ project.description }}
</slot>
</div>
</div>
</details>
</template>
</template>