Unveiling the Key to Utilizing $children in Vue 3 to Develop a Tabs Component

I am currently working on developing a Tabs component using Vue 3, similar to the one discussed in this relevant question.

<tabs>
   <tab title="one">content</tab>
   <tab title="two" v-if="show">content</tab> <!-- encountering issues here -->
   <tab :title="t" v-for="t in ['three', 'four']">{{t}}</tab> <!-- facing challenges here as well -->
   <tab title="five">content</tab>
</tabs>

Regrettably, the suggested solution does not function properly when there are dynamic Tabs inside, such as when there is a v-if condition or when the Tabs are rendered using a v-for loop - it fails to work.

To further illustrate my issue, I have provided a Codesandbox demonstration with .vue files:

https://codesandbox.io/s/sleepy-mountain-wg0bi?file=%2Fsrc%2FApp.vue

https://i.sstatic.net/EVwsr.png

I attempted to use onBeforeUpdate similarly to onBeforeMount, but unfortunately, that did not yield the desired results. In fact, although it did add new tabs, the tab order was altered.

The main challenge appears to be the inability to access or modify child data from the parent component in Vue 3 (similar to how it was done with $children in Vue 2.x). While someone recommended utilizing this.$.subtree.children, it was strongly discouraged and did not address my issue regardless of how I implemented it.

If anyone could provide guidance on how to make the Tab components inside Tabs reactive and update accordingly with v-if conditions, it would be greatly appreciated.

Answer ā„–1

It seems the issue lies in using the item index as the v-for loop's key.

The main problem is that you've applied v-for's key on a child element instead of the parent (specifically, on the <li> element).

<li v-for="(tab, i) in tabs">
  <a :key="i"> āŒ
  </a>
</li>

Furthermore, if items can be rearranged or removed from the backing array used in v-for, using the item index as the key may lead to issues with unique values. For example, removing an item could shift other items up in the list, causing a key clash and preventing proper re-rendering.

A better approach would be to use a unique identifier for each tab, such as the tab's title value. Below is the updated Tab.vue component:

// Tab.vue
export default {
  props: ["title"], šŸ‘ˆ
  setup(props) {
    const isActive = ref(false)
    const tabs = inject("TabsProvider")

    watch(
      () => tabs.selectedIndex,
      () => {
        isActive.value = props.title === tabs.selectedIndex
      }                        šŸ‘†
    )

    onBeforeMount(() => {
      isActive.value = props.title === tabs.selectedIndex
    })                       šŸ‘†

    return { isActive }
  },
}

Update your Tabs.vue template to utilize the tab's title as the key:

<li class="nav-item" v-for="tab in tabs" :key="tab.props.title">
  <a                                                     
    @click.prevent="selectedIndex = tab.props.title"
    class="nav-link"                          
    :class="tab.props.title === selectedIndex && 'active'"
    href="#"          
  >
    {{ tab.props.title }}
  </a>
</li>

Check out the demo

Answer ā„–2

This amazing solution was shared by @anteriovieira on the Vuejs forum and seems to be the perfect way to tackle the issue at hand. The key element that was missing in this puzzle was utilizing getCurrentInstance within the setup function.

To see the complete working code, simply click on this link:

https://codesandbox.io/s/vue-3-tabs-ob1it

I felt compelled to include it here for anyone who stumbles upon this page via Google while searching for a similar solution.

Answer ā„–3

By utilizing the accessible slots as $slots in the template (refer to the Vue documentation), you have the option to implement the following:

// Tabs component

<template>
  <div v-if="$slots && $slots.default && $slots.default()[0]" class="tabs-container">
    <button
      v-for="(tab, index) in getTabs($slots.default()[0].children)"
      :key="index"
      :class="{ active: modelValue === index }"
      @click="$emit('update:model-value', index)"
    >
      <span>
        {{ tab.props.title }}
      </span>
    </button>
  </div>
  <slot></slot>
</template>

<script setup>
  defineProps({ modelValue: Number })

  defineEmits(['update:model-value'])

  const getTabs = tabs => {
    if (Array.isArray(tabs)) {
      return tabs.filter(tab => tab.type.name === 'Tab')
    } else {
      return []
    }
</script>

<style>
...
</style>

An example of the Tab component could be:

// Tab component

<template>
  <div v-show="active">
    <slot></slot>
  </div>
</template>

<script>
  export default { name: 'Tab' }
</script>

<script setup>
  defineProps({
    active: Boolean,
    title: String
  })
</script>

To put it all together with an array of objects representing sections each with a title and a component:

...
<tabs v-model="active">
  <tab
    v-for="(section, index) in sections"
    :key="index"
    :title="section.title"
    :active="index === active"
  >
    <component
      :is="section.component"
    ></component>
  </app-tab>
</app-tabs>
...
<script setup>
import { ref } from 'vue'

const active = ref(0)
</script>

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

Adding hidden elements with jQuery: A comprehensive guide

I need assistance with adding the "is-hidden" class at this specific spot <span class="tag is-danger is-rounded is-small is-bolded span_notif_count ... "></span> As a beginner in jQuery, I am unsure about how to proceed. Could someon ...

Combining array of objects by various identifiers

I'm facing a situation like this: const idMappings = { // id: parentId "2": "1", "3": "1" } const inputData = [ { id: "1", data: [1], }, { id: "2", data: [2] }, { ...

What's the best way to implement asynchronous state updating in React and Redux?

In my React incremental-style game, I have a setInterval function set up in App.ts: useEffect(() => { const loop = setInterval(() => { if (runStatus) { setTime(time + 1); } }, rate); return () => clearInterval(lo ...

Looking for a way to access the source code of a QML method in C++?

I'm currently working on serializing objects to QML and I am looking for a way to retrieve the source code of functions defined within a QML object. Let's consider the following example in QML (test.qml): import QtQml 2.2 QtObject { functio ...

Unable to backtrack once a response has been sent

My Express application keeps crashing after sending a response to the client. It appears that the code continues to run even after the response has been returned. Can you please review the code snippet provided below? const EditUser = async (req, res) => ...

Extracting text from an array within Meteor

I am currently utilizing Meteor along with the ajduke:bootstrap-tagsinput plugin for managing tags on my platform. Check out the ajduke:bootstrap-tagsinput example page here For inserting tags as arrays, I am using the True Multiple Value feature mention ...

Asynchronous NestJs HTTP service request

Is there a way to implement Async/Await on the HttpService in NestJs? The code snippet below does not seem to be functioning as expected: async create(data) { return await this.httpService.post(url, data); } ...

Connection closure causing chaos in MongoDB structure

I have been facing issues with my mongodb client due to connection limits reaching 100. Whenever I try to close my connection after completing operations, I encounter this error: MongoError: Topology was destroyed Here is a snippet of my code: test.js - ...

jQuery unable to target Bootstrap button

I've been experiencing some trouble trying to attach a listener to a button I made with Bootstrap. <button type="button" id="buttonone" class="btn btn-default btn-lg good"> <span class="glyphicon glyphicon-minus" aria-hidden="true">< ...

Deliberately "locking" a JavaScript variable with a immediately-invoked function expression

While browsing through a blog post here that discusses creating a web scraper using node.js, I stumbled upon an intriguing piece of javascript that has left me somewhat perplexed. This particular snippet of code seems like something I could implement in my ...

Preventing PCs from accessing a specific webpage: A step-by-step guide

I am currently using the code below to block phones: if { /Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|Opera Mini/i .test(navigator.userAgent)) { window.location = "www.zentriamc.com/teachers/error ...

Sending chosen choice to controller method

I'm working with a table that contains inputs and angular models. <td> <select class="form-control" id=""> <option ng-model="mfrNo" ng-repe ...

What is the process for a webpage to save modifications made by JavaScript?

I am working on a simple web page with a form that contains checkboxes representing items from a database. When the submit button is clicked, these items may be retrieved. Additionally, there is an option to add a new item at the bottom of the page. My go ...

Tips on modifying the interface based on the selected entry using jQuery

I am attempting to display a text when different options are selected from the dropdown list. Here is the code for the drop-down list: <div class="dropdown"> <select class="form-control" id="ltype" name="ltype"> <option value=""&g ...

Next.js v13 and Firebase are encountering a CORS policy error which is blocking access to the site.webmanifest file

Background: I am currently developing a website using Next.js version 13 in combination with Firebase, and I have successfully deployed it on Vercel. Upon inspecting the console, I came across two CORS policy errors specifically related to my site.webmani ...

Roundabout Navigation Styles with CSS and jQuery

My goal is to implement a corner circle menu, similar to the one shown in the image below: https://i.stack.imgur.com/zma5U.png This is what I have accomplished so far: $(".menu").click(function(){ $(".menu-items").fadeToggle(); }); html,body{ colo ...

Issue with Sequential Drop Down List Functionality in ASP.Net MVC View Page

I am currently in the process of migrating code from one project to another. Although the code works fine in the original project, it is not functioning properly in the new one. Iā€™m uncertain if something was overlooked on my end. Within this code, ther ...

Encountering a TypeError with react-rte in Next.js: r.getEditorState is not a valid function

In my Next.js project, I am using a React RTE component. It is displaying correctly, but when I navigate to another component and then return using the browser's back button, I encounter the following error: Unhandled Runtime Error TypeError: r.getEd ...

Filtering options in a dropdown box with jQuery

I have implemented a feature where the content of one select box is filtered based on the option selected in another select box. Below is the code snippet that achieves this functionality: // Filter content based on the selected option in a region select ...

Having trouble getting the timer function to execute upon page load in JavaScript

My goal is to have my basic timer function activate when the page loads. However, I'm encountering issues with it not working as intended. I suspect there may be an error in the if else loop, possibly here: setTimeout(function(tag, sec), 1000);. How c ...