Getting a specific nested child component from the parent component's slot in the render function: A step-by-step guide

I have successfully implemented conditional rendering of a child component using the render function. Below is an example of the parent component with the child component:

<Parent :index="1">
  <Child> ... </Child>
  <Child> ... </Child>
  <Child> ... </Child>
</Parent>

Within the render function of the Parent component, I have written code to conditionally render the child component as shown below:

return () => {
  const content = slots.default?.();
  const children = content?.filter(child => child.type === Child);
  const childToRender = children?.[props.index] ?? h('div')
  return h('div', {}, () => [childToRender]);
}

This code is functioning correctly.

However, I now want to wrap one of the child components with another component. For instance, like this:

<Parent :index="1">
  <Child> ... </Child>
  <ChildWrapper> ... </ChildWrapper>
  <Child > ... </Child>
</Parent>

The ChildWrapper.vue component has the following structure:

<template>
  <Child> <slot/> </Child>
</template>

It's evident that the filter function (

content?.filter(child => child.type === Child)
) will not select the Child component within the ChildWrapper component.

When I inspect the ChildWrapper VNode's children property, it shows an object (with the default slot function) rather than an array containing the Child component that I expected. So my question is: how can I access the nested Child component?

Context of the issue

Imagine the parent component being a Tab or a Carousel component, and the child component being a TabItem or a CarouselItem component. The purpose of creating a ChildWrapper component is to customize some of the properties in the default Child component. Since the parent and child components belong to a component library that is unaware of the existence of ChildWrapper, the parent component cannot control how ChildWrapper should be rendered.

Answer №1

In my opinion, directly accessing specific, deeply nested child components from the parent component is not an advisable method. Child components can be nested to multiple levels, making direct access cumbersome.

Utilizing the provide and inject functions offers a more seamless way to achieve your desired outcome.

To further explain this concept, I will present a simple example below:

Parent component:


import { provide, ref } from 'vue'

const id = -1;
const current = ref(0)

function getId() {
  id++;
  return id;
}

provide('provide-key', {
  getId,
  current
})

Child component:


import { inject, computed } from 'vue'

const { getId, current } = inject('provide-key');
const id = getId();
const isShow = computed(() => id === current.value);

The provided code snippet may be incomplete, but it effectively conveys the central idea.

Answer №2

Two different approaches to handle rendering conditions.

  1. Create a state within your state manager:
exports defineState({
  visibleChildType: 'type'
})

In the Parent component, set the state value based on required logic.

Then, move the conditional rendering logic from the Parent to the child itself by importing the state:

// ChildComponent.vue
<template>
  <div v-if="type === visibleChildType" />
</template>

<script>
import visibleChildType from 'parentState'
</script>
  1. Place the wrapper inside the Child component:
// ChildComponent.vue
<template>
  <ChildWrapper v-if="needsWrapper">
    <div />
  </ChildWrapper>

  <div v-else=" />
</template>

This allows the child to determine when to wrap itself and avoids the need to refactor the parent component.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is it time to consider using a Promise in my JavaScript code given its rapid pace of execution?

I want to enhance the user experience of my web app by making elements fade away before being removed. However, I am facing an issue where my code is executing too quickly. I need it to wait for the element to "disappear" before actually removing it. Shoul ...

When a visitor accesses a webpage, enable port listening with Node.js and Express

Struggling to navigate the world of node.js, express, and port listening. My code is functioning properly, but only if I manually enter 'node index.js' in terminal to listen on port 3000... After hours of searching for a solution, my head is spi ...

What is the best way to resize images while also maintaining their ability to transition on hover?

Having an interesting dilemma here.. I'm trying to utilize this CSS code: .custom-forums { height: auto; width: 100%; border-image-source:transparent url("../images/forums.png") center top no-repeat; } in combination with: .custom-forum ...

Is there a way to use JQuery to dynamically generate a new select box with specific options?

I am looking to dynamically create a new select box based on the value selected in an existing select box using jQuery. Currently, the code we have implemented is not functioning correctly. <script src="http://code.jquery.com/jquery-1.5.min.js">&l ...

Unable to present information retrieved from REST API, despite data being provided by the server

I'm attempting to make a REST call using a token in the header to retrieve information. To include the required header token, my code is structured as follows within my restClient.js, app.js, and users.js files. //restClient.js import { jsonServerR ...

Displaying PHP content using JavaScript classes

I have a popup feature implemented in JavaScript and all the necessary scripts added to my HTML page. I am attempting to load a PHP page in the popup when the submit button of my form is clicked. The popup is functioning correctly for buttons like the one ...

Upon pressing the browser's back button, the page fails to automatically load with the updated URL

Providing a little context: Let's say I initially access the page using URL1. After that, I use window.history.pushState() to change the URL to URL2. Now, when I click the "back" button, I observe that the address bar displays URL1 again, but the pa ...

Preventing an RxJS Observable interval: A step-by-step guide

I am facing a unique scenario where I am using an RxJS interval, but I might need to abruptly stop it at any point. I thought there would be a simple method like cancel() or stop() similar to clearTimeout for intervals. Is there a way to stop an interval o ...

Guide on clearing filters in Angular 4

I'm facing an issue where I have implemented multiple filters but resetting them is not working as expected. showOnlyMyRequest(){ this.requests = this.requests.filter(request => request.requestedBy === 'John Doe'); } showAllReques ...

How can I implement jQuery autocomplete with customized settings?

In my Drupal project, I am looking to implement jQuery's auto complete feature to search for nodes based on their titles. I am having trouble finding examples that align with my specific requirements: The URL structure should be: /api/node/title/{wh ...

The delete function in aspx.cs never seems to be triggered from the .aspx file

I am encountering an issue with deleting items from a list, as my delete function in the .aspx.cs file is not being called. Below is my JavaScript code: function doTheDelete(doIDeleteExpenses) { if (selectedExpensesList.length > 0) { ...

Boost your website's loading time by utilizing the async and defer attributes

Looking to enhance the speed of my webpage, particularly focusing on improving initial page speed. Research suggests that using the async and defer attributes for JavaScript can be beneficial. All JavaScript scripts are currently placed just above the cl ...

The focus of the input is lost when the value is being edited, specifically in the case where ngFor and ng

I'm facing an issue with a simple list that binds two-way to a parameter hero in my hero.component.ts. Whenever I begin typing in the input field, it seems to lose focus and I have to click on it again. How can I ensure that users are able to edit th ...

What is the best way to incorporate a N/A button into the dateRangeFilter located in the header of a webix dataTable, enabling the exclusion of N/A values within that specific column?

`webix.ready(function(){ grid = webix.ui({ container:"tracker", editaction:"click", editable:true, view:"datatable", css:"webix_header_border", leftSplit:1, ...

Updating input value using React state

Hey there, I'm currently using a color picker to update my this.state.colorPicked value, which is working well. Now, I want to create a function that will concatenate the state's colorPicked value with the input field when called. If the input fi ...

Develop a line using botbuilder

Good day. I'm currently working on a vue js component that will function as a botbuilder. The main goal is to manipulate the cards displayed on the screen and establish links between them to define the flow of actions. Here's an image for referen ...

Choosing items in CSS

Here is a piece of HTML code to work with: <body> <pre class="typescript"> <span class="classDecl"> <span class="statement">export</span> <span class="keyword">class</span> ...

Dynamic Node.js server constantly updating

My goal is to create a dynamic Node.js Express server that updates live, possibly by creating a specific route like /update to load a new configuration file. My concern is that the server could be in any state when the update occurs. It's possible tha ...

What advantages can I expect for my client-rendered pages with Gatsby's Client Side Routing?

For a small site I developed, I utilized Gatsby for static content. However, for certain dynamically rendered content, I opted to employ client-only routes in Gatsby. Although I implemented a Header, Footer, and a specific font on my static site, these sa ...

Tips for incorporating `new google.maps.Marker()` with `vue2-google-maps` are as follows:1. First

Due to certain reasons, I find myself having to utilize new google.maps.Marker() with vue2-google-maps, but I'm unsure of where to begin as most documentation and tutorials use <GmapMarker ... /> in the HTML section instead. I've attempted ...