I have a custom component where the parent and an array of child components are tightly coupled and there is a "backdoor" communication between the parent and child components using the provide-inject feature provided by vue.
In the parent component, I would like to keep track of the child components in the order they are defined in the slot.
I know that on mounted, the child components are instantiated in the order they are defined. However, at runtime, a child component might be inserted in between first child component and second child component for example. The question is how do I find out the updated order of the child components in the parent slot?
I saw in the vue devtools, it can recognise the order of child and nested components in the Components tab and show it in a tree layout. How can I achieve something like that?
Here is the example code https://stackblitz.com/edit/vitejs-vite-pkqhkv?file=src%2FApp.vue
In the above example, if you click prev and next, it should show Slide 1 and Slide 3 as expected because Slide 2 is toggled 'off', but when you toggle on Slide 2, the order goes into 1, 3, 2 which is not what I want. The order should be 1, 2 and 3.
Obviously, the implementation of providing each child with a numbered index is not reliable as they can be registered at different times, therefore I want to implement differently where I can know the current order of the Child components at any given time.
Even your parent/child components are strongly coupled you better don't propagate the slide visibility state to the children. That's not a good design, too much leaking of the slide state. Make all slide logic inside the parent (a good design and encapsulation).
Parent.vue
<script setup>
import { ref, useSlots, watch, computed } from 'vue';
// the current component index
const current = ref(0);
const slots = useSlots();
// collect all slot child components
const $children = computed(() => slots.default().filter(vnode => vnode.type.render)); // remove all non-component vnodes
// watch for deletion of the current component and move to the next one
watch(() => $children.value.length, length => current.value < length || next(current.value - 1));
function prev(idx = current.value) {
if(--idx < 0) idx = $children.value.length - 1;
current.value = idx;
}
function next(idx = current.value) {
if(++idx >= $children.value.length) idx = 0;
current.value = idx;
}
defineExpose({
prev,
next,
});
</script>
<template>
<section>
<div>$children.length: {{ $children.length }}, current: {{ current }}</div>
<component :is="() => $children.filter((_, idx)=> idx === current)" />
</section>
</template>
App.vue
<script setup>
import Parent from './Parent.vue';
import Child from './Child.vue';
import { ref } from 'vue';
const toggleSlide2 = ref(false);
const toggleSlide3 = ref(true);
const parent = ref();
</script>
<template>
<div>
<section>
<button @click="parent.prev()">Prev</button>
<button @click="parent.next()">Next</button>
<button @click="toggleSlide2 = !toggleSlide2">Toggle Slide 2</button>
<button @click="toggleSlide3 = !toggleSlide3">Toggle Slide 3</button>
</section>
<Parent ref="parent">
<Child>
<h1>Slide 1</h1>
</Child>
<Child v-if="toggleSlide2">
<h1>Slide 2</h1>
</Child>
<Child v-if="toggleSlide3">
<h1>Slide 3</h1>
</Child>
</Parent>
</div>
</template>