Exploring limitless possibilities with Vue slot manipulation

Imagine I am looking to develop a multi-layered Component for reusability, similar to a 'Tab' UI.

This would allow developers to use it like this:

<tabs>
  <tab label="My First Tab">
    Content for first tab which could contain components, html, etc.
  </tab>
  <tab label="My Second Tab">
    More Content
  </tab>
</tabs>

The challenge arises when we need to access and manipulate the Tab components within the Tabs component without knowing how many tabs will be used.

I've experimented with methods like this.$children and this.slots.default but faced difficulties in accessing the Tab data for functionalities like showing and hiding tabs. It's even more challenging because I'm working with Typescript.

For example:

<template>
    <div class="tabs">
        <slot /> <!-- Location of the Tab's -->
    </div>
</template>

<script lang="ts">
import { Component, Vue } from "vue-property-decorator";

@Component({
    ...
})
export default class Tabs extends Vue {
    public tabs: any[] = [];
        
    public created() {
        this.tabs = this.$slots.default;
        
        let tab = this.tabs.find((obj: any, index: number) => {
            return index === 0;
        }); 
    }

    public select(index: number) {
        (this.$slots.default[index] as any).show = true;
    }
}
</script>

While exploring existing GitHub libraries for Vue Tabs, I found the logic to be quite complex. I believe there should be a simpler way to access child components using slots/children in Vue. Perhaps I'm being too optimistic.

If anyone has insights on how to handle, pass, or modify data within child slots dynamically, especially when the number of children is unknown, I would greatly appreciate the guidance.

Answer №1

If you are facing an issue where you need to wait for the tab components to be mounted, the Vue.js mounted lifecycle hook can help with this situation. However, it's important to note a potential issue with using the $children property, as mentioned in the Vue documentation:

There is no guarantee of order for $children, and it is not reactive. If you plan to use $children for data binding, consider using an Array along with v-for to generate child components, and use the Array as the source of truth.

Due to the lack of ordering guarantee, accessing this.$children[index] may not provide you with the expected tab element.

If you opt for the suggested approach of using v-for and an array, your implementation could resemble the following:

<template>
    <div class="tabs">
        <tab v-for="(tabName, index) in tabs" :key="tabName" :label="tabName" :ref="`tab-${index}`">
            <slot :name="tabName"></slot>
        </tab>
    </div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";

@Component({
    ...
})
export default class Tabs extends Vue {
    @Prop(Array) tabs: string[];

    public mounted() {
        this.select(0);
    }

    public select(index: number) {
        this.$refs[`tab-${index}`].show = true;
    }
}
</script>

I have placed the slot within a tab component to ensure that all child components will be nested inside a tab, guaranteeing that all children of the tabs component will indeed be tab components, and that your $refs will contain the expected tab properties. To use this setup, you would do the following:

<tabs :tabs="['My First Tab', 'My Second Tab']">
    <template slot="My First Tab">
        Content for the first tab, which may include various components, HTML elements, etc.
    </template>
    <template slot="My Second Tab">
        More content goes here
    </template>
</tabs>

Answer №2

One issue I've encountered is that the created() function for Tabs gets triggered before the Tab components are actually mounted, leading to a situation where they may or may not exist at that point. To address this, I implemented a Timeout to ensure that the initial selection works by using

(this.$children[index] as any)["show"] = true;

I'm curious if there's a way to determine when immediate child components have finished mounting. After doing some research, I haven't been able to find a definitive solution.

Answer №3

After some careful consideration, I managed to overcome the challenge by utilizing the updated life-cycle hook in conjunction with this.$nextTick(). This powerful combination allowed me to execute a specific task each time the component was updated, particularly when waiting for an axios post on the children elements.

In essence, the solution involved implementing the following logic:

mounted() {
  // This line accesses the children within the current component slots
  const tabs: Tab[] = this.$vnode.componentInstance.$children as Tab[];
  setTabs(tabs);
}

updated() {
  this.$nextTick(function () {
    const updatedTabs: Tab[] = this.$vnode.componentInstance.$children as Tab[];

    if (updatedTabs.length > this.tabs.length) setTabs(updatedTabs);
  });
}

setTabs(updatedTabs: Tab[]) {
  this.tabs = updatedTabs;
  // Additional operations can be included here, such as filtering or sorting
}

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

Can you explain the purpose and functionality of the following code in Typescript: `export type Replace<T, R> = Omit<T, keyof R> & R;`

Despite my efforts, I am still struggling to grasp the concept of the Replace type. I have thoroughly reviewed the typescript documentation and gained some insight into what is happening in that line, but it remains elusive to me. ...

The type definition file for 'jest' cannot be located, despite the fact that jest has been successfully installed

SOLUTION STRATEGY: If you encounter a similar issue and are looking for a more comprehensive solution rather than quick fixes, consider recreating the repository. While it involves more effort initially, it can prevent future issues. In my case, the repos ...

Error Encountered with Next.js 13.4.1 when using styled-components Button in React Server-Side Rendering

I am currently working on a React project using Next.js version 13.4.1 and styled-components. One problem I'm facing is with a custom Button component that I've created: import React from 'react'; import styled from 'styled-compone ...

Discover the combined type of values from a const enum in Typescript

Within my project, some forms are specified by the backend as a JSON object and then processed in a module of the application. The field type is determined by a specific attribute (fieldType) included for each field; all other options vary based on this ty ...

Remix.js is unable to perform a redirect action when utilizing the Form component

I've been searching through the Remix documentation, but I can't seem to find a solution to my issue. It appears that you're unable to redirect from an action when using the Form component in Remix. You can check out this StackBlitz example ...

Is it considered best practice to include try/catch blocks within the subscribe function in RxJs?

Imagine we have an Angular 2 application. There is a service method that returns data using post(), with a catch() statement to handle any errors. In the component, we are subscribing to the Observable's data: .subscribe( ()=> { ...

Encountered an issue trying to access undefined properties while reading 'PP'

I am trying to showcase the data retrieved from my JSON file. Here is a glimpse of the information stored in the JSON => Within DTA > PP , I am specifically interested in displaying the variable NOMFAMILLE. An error message has caught my attentio ...

Utilize the up and down arrow keys to scroll through a description list in React

If you want to navigate through the list of Description Details using the tab and shift tab keys, it can be done easily. The default behavior allows for smooth navigation. <dl> <dt style={{ textAlign: "center" }}>Beast of Bodmin< ...

Custom Typescript type that runs concurrently with the base type is disregarded

Assumption: When creating a custom type that mirrors an existing type, the expectation is for variables assigned to that type to maintain it and not default back to the base type. In the function f provided below, the expected return type should be Dog ins ...

Struggling to iterate through JSON data in Office Scripts?

My task involves parsing JSON data in Office Scripts to extract the headings and row details on a spreadsheet. While I have successfully fetched the data, I am encountering an error message stating that my information is not iterable at the "for" loop. ...

Is there a way to bring in both a variable and a type from a single file in Typescript?

I have some interfaces and an enum being exported in my implementation file. // types/user.ts export enum LoginStatus { Initial = 0, Authorized = 1, NotAuthorized = 2, } export interface UserState { name: string; loginStatus: LoginStatus; }; ex ...

What is the best approach for handling server-side validation errors in Angular when making an HTTP call?

After following different tutorials, I have created a service that can transmit login details to the backend for validation and processing. Although I am able to generate appropriate error codes based on user input, I find myself wondering what to do next. ...

Automatically divide the interface into essential components and additional features

Consider the following interfaces: interface ButtonProps { text: string; } interface DescriptiveButtonProps extends ButtonProps { visible: boolean, description: string; } Now, let's say we want to render a DescriptiveButton that utilize ...

The seamless fusion of Express with Typescript

Hello and thank you for taking the time to assist me. I recently completed a Cron app using Node.JS. I wanted to add a website hosted by the Node.js server with Express. I developed this TypeScript website in a separate folder, but encountered errors when ...

The confirm alert from Material UI is being obscured by the dialog

How can I ensure that a material ui dialog does not hide the alert behind it when confirming an action? Is there a way to adjust the z index of the alert so that it appears in front of the dialog? import Dialog from "@material-ui/core/Dialog"; i ...

The Google Chrome console is failing to display the accurate line numbers for JavaScript errors

Currently, I find myself grappling with debugging in an angular project built with ionic framework. Utilizing ion-router-outlet, I attempt to troubleshoot using the Google Chrome console. Unfortunately, the console is displaying inaccurate line numbers mak ...

Sanity.io's selection of schema field types for efficient and convenient

Hey there, guys! I recently started using Sanity.io and I'm curious whether there's a way to enhance my code efficiency and reuse certain fields across different schemas. I had an idea that goes something like this: cars.ts: export default { ...

When redirecting to the same component, Vue.js hooks are not triggered

In my Vuejs project, I have two pages located at the same level with a common component. However, when I redirect from post/new to post/edit/:id, the beforeMount method is not being called. redirect() { this.$router.push({path: `home/post/edit/${this ...

The proper method for specifying contextType in NexusJS when integrating with NextJS

I am currently facing a challenge while trying to integrate Prisma and Nexus into NextJS. The issue arises when I attempt to define the contextType in the GraphQL schema. Here is how I have defined the schema: export const schema = makeSchema({ types: [ ...

"This error message states that the use of an import statement outside a module is not allowed

After searching for a solution without any luck, I decided to start a new discussion on this topic. Currently, I am working on azure functions using Typescript and encountering the following error: import { Entity, BaseEntity, PrimaryColumn, Column, Many ...