Скажем, я хочу создать многослойный компонент для повторного использования, например, 'Tab' Ui.

Итак, использующий разработчик мог написать:

<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>

По сути, они будут использовать вкладки и компоненты вкладки таким образом, что я понятия не имею, сколько компонентов вкладок разработчик будет использовать в вкладках. Как мне получить доступ к компонентам вкладок, например, чтобы показать и скрыть их функциональность пользовательского интерфейса вкладки?

Я пробовал использовать this.$children и this.slots.default, но не смог получить доступ к данным вкладки, чтобы показать или скрыть их. Честно говоря, я пишу на Typescript, и из-за этого проблема может быть более сложной.

Что-то вроде:

<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;
        // this.tabs is a set of VNode's, 
        //    I can't figure out how to access the components
        //    I see a prop 'componentInstance' but when I try to
        //    access it, it says undefined
        // this.select(0);
        let tab = this.tabs.find((obj: any, index: number) => {
            return index === 0;
        }); // tab is undefined :(
    }

    public select(index: number) {
        (this.$slots.default[index] as any).show = true;
        // Accessing Vnode instead of Component :(
        // this.$children[0]["show"] = true; // Read only :(
        // this.tabs[index].show = true; // blerrggg :(

        // Vue.nextTick();
    }
}
</script>

Я просмотрел некоторые библиотеки GitHub для вкладок во Vue, и логика кажется очень сложной. Я предполагаю, что из-за наличия слотов / дочерних элементов существует более простой подход к доступу к дочерним компонентам. Может быть, это слишком обнадеживает с моей стороны.

Кто-нибудь знает, как получить доступ, передать или изменить данные в дочернем слоте, если вы не знаете, сколько дочерних элементов будет там (т.е. вы явно не записали их в родительский компонент)?

3
dmcblue 15 Сен 2018 в 23:32

2 ответа

Лучший ответ

Если проблема в том, что вам нужно подождать, пока не будут смонтированы компоненты вкладки, жизненный цикл смонтирован крючок существует для такого рода вещей. Однако существует потенциальная проблема с использованием свойства $children, как указано в Vue документы :

Обратите внимание, что для $ children нет гарантии порядка, и он не является реактивным. Если вы обнаружите, что пытаетесь использовать $ children для привязки данных, подумайте об использовании Array и v-for для генерации дочерних компонентов и используйте Array как источник истины.

Поскольку порядок не гарантирован, this.$children[index] не гарантирует, что вы получите нужную вкладку.

Если бы вы использовали предложенный подход, используя v-for и массив, это могло бы выглядеть примерно так:

<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>

Я помещаю слот внутри вкладки, чтобы вы знали, что все дочерние компоненты будут заключены в компонент вкладки, что означает, что все дочерние компоненты tabs гарантированно будут компонентами tab, а ваши {{X2 }} все будут иметь ожидаемые свойства tab. Вы бы использовали это так:

<tabs :tabs="['My First Tab', 'My Second Tab']">
    <template slot="My First Tab">
        Content for first tab which could contain components, html, etc.
    </template>
    <template slot="My Second Tab">
        More content
    </template>
</tabs>
3
Tomhah 16 Сен 2018 в 00:34

Часть моей проблемы (кажется) заключается в том, что функция created() для вкладок срабатывает до того, как компоненты вкладки смонтированы, поэтому они могут существовать, а могут и не существовать. Вызвав первоначальный выбор с таймаутом, я смог использовать

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

Кто-нибудь знает, как проверить, когда (немедленные) дочерние компоненты смонтированы? Я немного поискал и не смог найти правильный ответ.

1
dmcblue 15 Сен 2018 в 21:23