typescript - Åtkomst till godtyckliga platser i Vue
Säg att jag vill skapa en flerskiktad komponent för återanvändning, som en 'Tab'-UI.
Så att den användande utvecklaren kunde skriva:
<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>
I princip skulle de använda Tabs och Tab-komponenterna på ett sätt så att jag inte har någon aning om hur många Tab-komponenter utvecklaren kommer att använda i Tabs. Hur får jag åtkomst till Tab-komponenterna för att till exempel visa och dölja dem per tab UI-funktionalitet?
Jag har försökt använda this.$children och this.slots.default men kunde faktiskt inte komma åt flikdata för att visa och dölja den. För att vara rättvis, jag skriver i Typescript och problemet kan vara mer komplicerat på grund av det.
Något som:
<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>
Jag har tittat på några GitHub-bibliotek för flikar i Vue och logiken verkar väldigt komplicerad. Jag antar att från existensen av slots/children finns det ett mer rakt på sak sätt att komma åt barnkomponenter. Kanske är det förhoppningsfullt av mig.
Vet någon hur man får åtkomst till, skickar eller ändrar data i en barnslot om man inte vet hur många barn som kommer att finnas (dvs du skrev dem inte uttryckligen i föräldrakomponenten)?
Svar #1
Om problemet är att du måste vänta tills tabbkomponenterna är monterade, finns livscykel-hooken mounted för den här typen av saker. Det finns dock ett potentiellt problem med att använda $children-egenskapen, som beskrivs i Vue-dokumentationen:
Observera att det inte finns någon ordningsgaranti för $children, och det är inte reaktivt. Om du märker att du försöker använda $children för databindning, överväg att använda en Array och v-for för att generera barnkomponenter, och använd Arrayen som sanningskälla.
Eftersom ordningen inte är garanterad, är this.$children[index] inte garanterad att ge dig fliken du förväntar dig.
Om du skulle gå med det föreslagna tillvägagångssättet, att använda v-for och en array, kan det se ut ungefär så här:
<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>
Jag lade sliden inuti en flik så att du vet att alla barnkomponenter kommer att omslutas av en flikkomponent, vilket innebär att alla tabs barn är garanterade att vara tab komponenter, och dina $refs kommer alla att ha de förväntade tab egenskaperna. Du skulle använda detta på följande sätt:
<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>
Svar #2
En del av mitt problem (det verkar) är att created()-funktionen för flikar körs innan Tab-komponenterna monteras, så de kanske finns eller inte. Genom att anropa den initiala valet med en Timeout kunde jag använda
(this.$children[index] as any)["show"] = true;
Vet någon hur man kontrollerar när (omedelbara) barnkomponenter monteras? Jag har sökt lite och kunde inte hitta ett korrekt svar.
