Vue: nesting components within components

Looking for a clever solution to create a nested component system with efficient rendering? Check out the code snippet below:

custom-tab.vue (child component)

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

<script>
    export default {
        name: 'CustomTab',
        props: ['title']
    }
</script>

custom-tabs.vue (container component)

<template>
    <div class="custom-tabs-switchers">
        <b
            v-for="(tab, index) in tabs"
            :key="`tab-${index}`"
        >
            {{ tab.componentInstance.props.title }}
        </b>
    </div>
    <div class="custom-tabs-contents">
        <div class="custom-tabs-contents-item"
            v-for="(tab, index) in tabs"
            :key="`tab-content-${index}`"
        >
            <!-- HOW TO DISPLAY TAB CONTENT HERE??? -->
        </div>
    </div>
</template>
<script>
    export default {
        name: 'CustomTabs',
        computed () {
            tabs () {
                return this.$slots.default
            }
        }
    }
</script>

example-page.vue (component with usage example)

<template>
    <custom-tabs>
        <custom-tab title="first tab"><p>Content of Tab #1</p></custom-tab>
        <custom-tab title="second tab"><p>Content of Tab #2</p></custom-tab>
        <custom-tab title="third tab"><p>Content of Tab #3</p></custom-tab>
    </custom-tabs>
</template>

Answer №1

The secret to finding a solution is utilizing the render function of the component (https://v2.vuejs.org/v2/guide/render-function.html) in order to add some personalization. Here's an example:

export default {
  name: 'Tabs',
  render: function (createElement) {
    const buttons = []
    const tabs = []
    const children = this.$slots.default // using shortcut

    for (let i = 0; i < children.length; i++) {
      buttons.push(createElement(
        'button',
        children[i].componentOptions.propsData.title
      ))
      tabs.push(createElement(
        'div',
        {
          class: i === 0 ? 'active tab' : 'tab',
          attrs: {
            id: `tab-${i}`
          }
        },
        children[i].componentOptions.children
      ))
    }
    const buttonsContainer = createElement('div', { class: 'buttons' }, buttons)
    const tabsContainer = createElement('div', tabs)

    const root = createElement('div', { class: 'tabs' }, [buttonsContainer, tabsContainer])
    return root
  }
}

I'm uncertain about the VNode API (

children[i].componentOptions.propsData.title
-- does it work correctly?) but from my own experience, the solution works and seems to be the right approach.

Cheers!

Answer №2

In order to render the contents of the slot, you do not need to use v-for.

Vue.component('Tabs', {
  template: `
    <div class="tab-container">
      <slot></slot>
    </div>
  `
})

Vue.component('Tab', {
  template: `
    <div class="tab">
      <strong>{{title}}</strong>
      <slot></slot>
    </div>
  `,
  
  props: ['title']
})

new Vue().$mount('#app');
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <tabs>
    <tab title="tab 1">
      <p>Tab #1 content</p>
    </tab>
    <tab title="tab 2">
      <p>Tab #2 content</p>
    </tab>
    <tab title="tab 3">
      <p>Tab #3 content</p>
    </tab>
  </tabs>
</div>

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 anyone else experiencing issues with loading a font from a CDN? It works perfectly fine on Chrome Browser and Safari for iOS Simulator, but for some reason, it's not loading

It's driving me a bit crazy as I'm not sure where I've gone wrong. I'm currently working with NextJS and have loaded this font into my <Link />. While it displays perfectly on Chrome and Safari in the iOS simulator, it fails to l ...

Troubleshoot: Unable to utilize mapActions with Vuex modules

Having trouble using mapActions to reference actions in my modules. The Vuex docs say that module actions are not namespaced by default, so they should be accessible like main store actions. Here's how I have things set up: Store import * as ModuleA ...

Is it possible to create a bot that's capable of "hosting events" using Discord.js?

I am searching for a solution to host "events" using Discord.js. After some research, I stumbled upon this. Although it seems to be exactly what I am looking for, the website does not provide any code examples to help me try and replicate its functionali ...

Acquire the Dynamic HTML Tag from the source code of the page

I am currently using Selenium to develop a bot that will automate a task by fetching a report from a website. I must admit, I am not well-versed in HTML or Javascript, so please forgive any misuse of terms. Here is the problem at hand: Using Python, I wri ...

Recognizing the click event on the checkbox within a <TableRow/> in ReactJS with MaterialUI

Using ReactJS and MaterialUI's component for tables, I have successfully created a table with two rows, each containing a checkbox. Now, the challenge is to create a method that can recognize when a checkbox is clicked and provide information about th ...

Cloud Firestore trigger fails to activate Cloud function

I am facing an issue with triggering a Cloud Function using the Cloud Firestore trigger. The function is supposed to perform a full export of my sub collection 'reviews' every time a new document is added to it. Despite deploying the function suc ...

Tips for efficiently exporting and handling data from a customizable table

I recently discovered an editable table feature on https://codepen.io/ashblue/pen/mCtuA While the editable table works perfectly for me, I have encountered a challenge when cloning the table and exporting its data. Below is the code snippet: // JavaScr ...

Executing a JQuery function from varying environments

While this question may seem basic, I am having trouble understanding the behavior described. I have written some JavaScript code and I am puzzled why the second call to foo does not work. You can find the code in this JSFiddle link. $.fn.foo = function( ...

Parsing and Displaying JSON Data from a Python DataFrame in D3

Trying to create a stock chart, I encountered an issue with parsing the json file output by my python dataframe. The example code from http://bl.ocks.org/mbostock/3884955 does not seem to fit the format of my data: The json looks like this: var dataset = ...

Upon refreshing the page, nested React routes may fail to display

When I navigate to the main routes and their nested routes, everything works fine. However, I encounter an issue specifically with the nested routes like /register. When I try to refresh the page, it comes up blank with no errors in the console. The main r ...

What steps can be taken to diagnose the cause of a failed Jquery AJAX request?

I am attempting to utilize the Yahoo Finance API to retrieve data in CSV format through Javascript. However, my current implementation shown below is not successful. $.ajax({ type: "GET", url: "http://finance.yahoo.com/d/quotes.csv?s=RHT+MSFT&f=sb2b3j ...

Initiating a click function for hyperlink navigation

Here is the HTML and JavaScript code that I am currently working with: <!DOCTYPE html> <html> <head> <script src="http://code.jquery.com/jquery-3.3.1.min.js"></script> </head> <body> <a href="#f ...

Drawing on Canvas with Html5, shifting canvas results in significant issues

I've been working on developing an HTML5 drawing app, and while I have all the functionality sorted out, I'm facing challenges during the design phase. My main issue is centered around trying to make everything look visually appealing. Specifical ...

Nuxt: Configure Axios requests to have their origin set based on the current domain

Currently, I am utilizing @nuxtjs/proxy for making proxy requests. The setup in nuxt.config.js is working perfectly fine. nuxt.config.js proxy: { '/api/': { target: 'api.example.com', headers: { 'origin': &apo ...

Dynamically loading components in Vue Router depending on the route parameter

I am currently in the process of developing an editor application that can accommodate multiple design templates. Each template comes with its own unique set of fields, and therefore has a designated .vue file. My goal is to dynamically load the appropria ...

What is the best way to extract the value from a Material UI Slider for utilization?

I am looking to capture the value of the slider's onDragStop event and store it as a const so that I can use it in various parts of my code. However, I am unsure about how to properly declare my const sliderValue and update it. Any guidance on where a ...

Basic JavaScript string calculator

I'm in the process of creating a basic JavaScript calculator that prompts the user to input their name and then displays a number based on the input. Each letter in the string will correspond to a value, such as a=1 and b=2. For example, if the user e ...

Turning HTML into an image with the power of JavaScript

Utilizing html2canvas to convert a div into an image, everything is functioning properly except for the Google font effect. The image shows how it eliminates the effect from the text. https://i.stack.imgur.com/k0Ln9.png Here is the code that I am using f ...

Nested Angular click events triggering within each other

In my page layout, I have set up the following configuration. https://i.stack.imgur.com/t7Mx4.png When I select the main box of a division, it becomes highlighted, and the related department and teams are updated in the tabs on the right. However, I also ...

Using Javascript to automatically submit a form when the enter key is pressed

Can anyone help me with a password form issue? I want the enter key to trigger the submit button but it's not working for me. I understand that the password can be viewed in the source code, this is just for practice purposes. <!DOCTYPE html> & ...