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

Issue with accessing undefined property in Angular 2+ using Typescript

In my Angular 7 project, I am retrieving data from a service which looks like this: {name: "peter", datetime: 1557996975991} I have a method that is supposed to retrieve this data: myMethod() { this.myService.getdata().subscribe((res) = ...

Building a PathString Tree

I encountered a similar issue like the one discussed in this post (Get a tree like structure out of path string). I attempted to implement the suggested solution but I am facing difficulties getting it to work within an Angular context. The concept involv ...

Precisely outline the function type that operates on an object, makes changes to its values, and then outputs the object in TypeScript

Can anyone help me create a function that generates a new object with the same keys as the input object, but with all values set to null? Here's the existing code: function nullify(arg) { var returnObj = {} for (var key in arg) { returnObj[ ...

An issue occurred while compiling the 'ToastContainer' template. Decorators do not support function calls, and the call to 'trigger' caused an error

When I run ng serve and ng build, there are no errors. However, when I run ng build --prod, I encounter this error. Can anyone help me fix it? ERROR in Error during template compile of 'ToastContainer' Function calls are not supported in decor ...

Guide to importing a JavaScript module as any type without using a declaration file (d.ts)

Looking to bring a js module into my ts app. Is there a way to achieve this without creating a d.ts file? If not, how can it be declared as any in the d.ts file? Currently using //@ts-ignore to ignore the error. Appreciate any help! ...

Creating a template / component in Vue.js 2 with two identical files

Currently using Vue.js 2.0, I find myself duplicating the exact same code in two different files, with only the ID_SPECIFIC_TO_THIS_BLOCK being different. As a Vue beginner, I am curious to know if there is a way to create a reusable template that can be u ...

requirements for navigating within an Angular application

Initially, if the first user is already logged in on the first tab and is redirected to the dashboard, when that same user opens a second tab in the same browser (by pasting the login URL), they are automatically redirected to the dashboard without having ...

Troubleshooting an Angular application in Intellij using Chrome on a Windows operating system

I've been searching for a long time for a way to debug an Angular app in IntelliJ using Chrome on Windows. So far, I have not been successful in attaching a debugger to Chrome. I have tried launching Chrome with --remote-debugging-port=9222 and numer ...

Working with both vue 2 and vue 3 simultaneously on a single machine

Starting my first vue 3 project and running into issues with installing vue-cli. The documentation mentions that for Vue 3, I should be using Vue CLI v4.5 which can be found on npm as @vue/cli. To upgrade, it is necessary to reinstall the latest version of ...

Choosing the Active Browser Tab while Modal is Open in Angular

In my current situation, I have implemented a function in the first tab that displays a modal or component after 5 seconds: ngOnInit() { setTimeout(() => { this.openDialog(); }, 5000); } openDialog() { this.dialog.open(.....); } However, if ...

Material-UI and TypeScript are having trouble finding a compatible overload for this function call

Currently, I'm in the process of converting a JavaScript component that utilizes Material-ui to TypeScript, and I've encountered an issue. Specifically, when rendering a tile-like image where the component prop was overridden along with an additi ...

Tips for formatting dates in Angular 6

I am currently working on a function that displays real-time dates based on user input. Currently, when the user enters the input, it is displayed in the front end as follows: 28.10.2018 10:09 However, I would like the date to change dynamically based on ...

Can you explain the distinction, if one exists, between a field value and a property within the context of TypeScript and Angular?

For this example, I am exploring two scenarios of a service that exposes an observable named test$. One case utilizes a getter to access the observable, while the other makes it available as a public field. Do these approaches have any practical distincti ...

Tips for transitioning from Angular to Angular 2: Overcoming key challenges

Our current Angular project is highly developed, but with the emergence of Angular 2 and its advanced features and improved performance, we are considering migrating our existing work. However, we are concerned about the potential challenges that may ari ...

A new feature introduced in TypeScript, expression-level syntax was not present until it was added in JavaScript

Celebrating a Decade of TypeScript remarked that "It’s quite remarkable how the design goals set for TypeScript have stood the test of time." I am particularly intrigued by the goal of "Avoid adding expression-level syntax." One user even brought up thi ...

When using TypeScript, how can I effectively utilize the Component property obtained from connect()?

Software Development Environment TypeScript 2.8 React 16 Redux Foo.tsx interface IFooProps{ userId:number } class Foo extends Component<IFooProps>{ render(){ return <p>foo...</p> } } const mapStateToProps = (state: I ...

An instance of an object is being added instead of parameters

I'm having some trouble making a server call using promises. Whenever I try to add my parameters, they end up showing as 'object%20Object' Here's the code snippet for the call: import { Injectable } from '@angular/core'; imp ...

The object[] | object[] type does not have a call signature for the methods 'find()' and 'foreach()'

Here are two array variables with the following structure: export interface IShop { name: string, id: number, type: string, } export interface IHotel { name: string, id: number, rooms: number, } The TypeScript code is as shown below ...

Is it possible to define react-router v6 routes within a functional component?

I have developed an application that requires users to log in before accessing it. I attempted to implement it using the following code: import React, {useState} from 'react'; import {Route, Routes} from 'react-router-dom'; import type ...

Error encountered with the Angular 2 routing system

Currently, I am facing an issue with my Angular 2 router module. Whenever I try to access the link /city, I encounter an error message saying 'ERROR Error: Uncaught (in promise): Error: Cannot activate an already activated outlet Error: Cannot activat ...