I have a Header and a Main page on Top page now, and I have three Tab functions in the Header component. I want to switch displays(A, B, C) in the Main page when I press each Tab (A, B, C). I am trying to change it using Emit but it doesn't work. If you could give me some advice, I really appreciate it.
Header.vue
<script setup >
const tabs = [
{name: 'A', comp: A},
{name: 'B', comp: B},
{name: 'C', comp: C}
];
interface Emits {
(e: "clicked", value: any);
}
const clicked = defineEmits<Emits>();
</script>
<template>
<div class="tabs" >
<button v-bind:class="['tab-button', A]" @click="$emit('clickedA',tabs[0])">A</button>
<button v-bind:class="['tab-button', B]" @click="$emit('clickedB',tabs[1])">B</button>
<button v-bind:class="['tab-button', C]" @click="$emit('clickedC',tabs[2])">C</button>
</div>
</template>
Mainpage.vue
<script>
let currentTab = A
function getValue(event) {
currentTab = event
}
</ script>
<template>
<Header />
<div>
<component :is="currentTab" @clicked="getValue"></component>
</div>
</template>
I would have the 3 buttons all call the same emit method, passing in the component String as a parameter of that emit:
<button v-bind:class="['tab-button']"
@click="$emit('myClicked', tabs[0])">
A
</button>
<button v-bind:class="['tab-button']"
@click="$emit('myClicked', tabs[1])">
B
</button>
<button v-bind:class="['tab-button']"
@click="$emit('myClicked', tabs[2])">
C
</button>
Then, in the parent component, you could listen for that emit and that one alone:
<Header @myClicked="getValue" />
and in script:
function getValue(tab) {
currentTab.value = tab.name;
}
For example:
Mainpage.vue
<template>
<div>
<Header @myClicked="getValue" />
<div>
<component :is="currentTab"></component>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import Header from "./Header.vue";
let currentTab = ref("A");
function getValue(tab) {
currentTab.value = tab.name;
}
function getTab(tab) {
currentTab.value = tab.name;
}
</script>
Header.vue
<script setup>
const tabs = [{ name: "A" }, { name: "B" }, { name: "C" }];
const emit = defineEmits(["myClicked"]);
</script>
<template>
<div class="tabs">
<button v-bind:class="['tab-button']"
@click="$emit('myClicked', tabs[0])">
A
</button>
<button v-bind:class="['tab-button']"
@click="$emit('myClicked', tabs[1])">
B
</button>
<button v-bind:class="['tab-button']"
@click="$emit('myClicked', tabs[2])">
C
</button>
</div>
</template>
A, B, and C.vue:
<template>
<h2>Component A</h2>
</template>
<template>
<h2>Component B</h2>
</template>
<template>
<h2>Component C</h2>
</template>
main.js
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
import A from "./components/A.vue";
import B from "./components/B.vue";
import C from "./components/C.vue";
const app = createApp(App);
app.component("A", A);
app.component("B", B);
app.component("C", C);
app.mount('#app')
OCD cleaning up code a little more.
Using v-for in the Header.vue file to simplify the creation of buttons, and passing the name field into the emit's method parameter changes the following vue files:
Header.vue
<script setup>
const tabs = [
// assuming that in "real" code,
// the display name and component name values would likely be different
{ name: "A", compName: "A" },
{ name: "B", compName: "B" },
{ name: "C", compName: "C" },
];
const emit = defineEmits(["myClicked"]);
</script>
<template>
<div class="tabs">
<button
v-for="tab in tabs"
:key="tab.name"
@click="$emit('myClicked', tab.compName)"
>
{{ tab.name }}
</button>
</div>
</template>
Mainpage.vue
<template>
<div>
<Header @myClicked="getValue" />
<div>
<component :is="currentTab"></component>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import Header from "./Header.vue";
let currentTab = ref("A");
function getValue(tabName) {
currentTab.value = tabName;
}
function getTab(tab) {
currentTab.value = tab.name;
}
</script>