Never mind, I just used Pinia instead & fixed the issue that way, no need to pass the emit upstream.
Vue defineEmits (upstream emitting)
Hello, I am trying to work out how to use emits in Vue. I have a component that is nested 2 deep, that is emitting a flash message when a button is clicked.
This is my component (TeamMember.vue) that emits the flash message:
<template>
<tr class="bg-gray-100 px-12">
<td class="text-xl font-medium flex items-center gap-x-4 px-6 py-4">
<img :src="`https://i.pravatar.cc/50?u=${email}`" alt="John Doe" class="w-10 h-10 rounded-xl">
<span>{{ props.name }}</span>
</td>
<td class="text-gray-500 px-6 py-4">
<span>{{ props.email }}</span>
</td>
<td class="px-6 py-4">
<button @click="update(useFlash('clicked'))" v-if="props.status === 'Active'" class="bg-green-400 hover:bg-green-500 text-white px-4 py-2 rounded">
{{ props.status }}
</button>
<button v-else class="bg-yellow-400 hover:bg-yellow-500 text-white px-4 py-2 rounded">
{{ props.status }}
</button>
</td>
</tr>
</template>
<script setup>
import {useFlash, flash} from "@/mixins/flash";
let props = defineProps({
modelValue: {
type: Object,
show: Boolean,
message: String,
},
name: String,
email: String,
status: String,
})
let emit = defineEmits(["update:modelValue"]);
function update(flash) {
emit("update:modelValue", flash);
}
</script>
I have checked & it is working, when I click the button the flash.message value changes to "clicked" and flash.show value to true.
Then the FlashMessage component is the following:
<template>
<div
v-if="props.flash.show"
class="absolute p-4 m-2 rounded-md bkg-green text transition-fade opacity-0"
>
{{ props.flash.message }}
</div>
</template>
<script setup>
import { watch } from "vue";
// define the props
let props = defineProps({
flash: {
type: Object,
show: Boolean,
message: String,
},
});
// watch the value of show and if it is true, set it to false after 3 seconds
watch(
() => props.flash.show,
(show) => {
if (show) {
console.log("show is true");
setTimeout(() => {
props.flash.show = false;
}, 3000);
}
}
);
</script>
As I mentioned this is being rended, upstream 2 component, in the root App.vue. This is what my App.vue looks like at the moment.
<script setup>
import TeamView from "@/views/TeamView.vue"
import FlashMessage from "@/components/FlashMessage.vue";
import { flash } from "@/mixins/flash.js";
</script>
<template>
<div class="grid m-2 p-2">
<div class="place-self-center">
<TeamView />
<FlashMessage v-model="flash" :flash="flash" />
</div>
</div>
</template>
This is the mixin where flash & useFlash are being exported/imported from (mixins/flash.js)
import {ref} from "vue";
export let flash = ref({
show: false,
message: "",
});
export function useFlash(message) {
flash.value.show = true;
flash.value.message = message;
}
The View / Component in the middle is the following (TeamView.vue)
<script setup>
import TeamMember from "@/components/TeamMember.vue";
</script>
<template>
<main style="width: 768px">
<header class="flex justify-between">
<div>
<button
class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded disabled:opacity-50 disabled:cursor-not-allowed"
:disabled="true">
Add Member (0 Spots Left)
</button>
</div>
<div class="bg-gray-100 py-2 rounded px-4">
<h3 class="inline-flex items-center text-3xl relative">
<svg viewBox="0 0 200 200" width="30" height="30" xmlns="http://www.w3.org/2000/svg">
<circle cx="100" cy="100" fill="yellow" r="78" stroke="black" stroke-width="3"/>
<g class="eyes">
<circle cx="61" cy="82" r="12"/>
<circle cx="127" cy="82" r="12"/>
</g>
<path d="m136.81 116.53c.69 26.17-64.11 42-81.52-.73" style="fill:none; stroke: black; stroke-width: 3;"/>
</svg>
<span>Team Smiley</span>
<div
class="bg-green-400 w-5 h-5 text-xs text-white rounded-full flex justify-center items-center absolute -right-5 -top-3">
1
</div>
</h3>
</div>
</header>
<div class="place-self-center flex flex-col gap-y-3 mt-4">
<table class="table-fixed border-spacing-2 border-separate">
<thead>
<tr>
<th class="text-left px-6 py-2">Name</th>
<th class="text-left px-6 py-2">Email</th>
<th class="text-left px-6 py-2">Status</th>
</tr>
</thead>
<tbody>
<TeamMember name="John Doe" email="[email protected]" status="Active" />
<TeamMember name="Jane Doe" email="[email protected]" status="Pending" />
<TeamMember name="James Doe" email="[email protected]" status="Active" />
<TeamMember name="Jill Doe" email="[email protected]" status="Pending" />
<TeamMember name="Jack Doe" email="[email protected]" status="Active" />
</tbody>
</table>
<p class="text-gray-600 italic text-right">There are no remaining team spots, Upgrade to add more.</p>
</div>
<footer class="mt-12 bg-gray-100 py-4 text-center">
<h5 class="font-semibold text-lg">Smiley (5 Member Team) </h5>
<a href="#" class="text-blue-500 hover:text-blue-600">Terms</a> |
<a href="#" class="text-blue-500 hover:text-blue-600">Privacy</a> |
<a href="#" class="text-blue-500 hover:text-blue-600">Contact</a>
</footer>
</main>
</template>
How do I make it so that the emitted flash message makes it all the way up to the component where the flash message is being rendered?
Do I need to add anything in the in between component (like the reverse of prop drilling) to send the emitted object upstream?
Thanks in advance!
Please or to participate in this conversation.