I'm trying to make a toggle button so that a hamburger menu opens when clicked.
I made the boolean "clicked" property in "App.vue", passed it down to "Navbar.vue", and now I want to be able to click in the navbar to toggle the "clicked" property to "true" or "false" to make the backdrop and drawer show or not show.
I tried to use an "emit", and it seems to work, but the template isn't responding to the "clicked" variable and is showing even though it's false.
In the code below, what part did I get wrong? How do you implement conditional rendering with props? Can someone help?
App.vue
<template>
<NavBar :clicked="clicked" @toggleDrawer="toggleMenu()" />
<BackDrop :clicked="clicked" />
<SideDrawer :clicked="clicked" />
<router-view></router-view>
</template>
<script>
export default {
name: "App",
components: { NavBar, BackDrop, SideDrawer },
setup() {
const clicked = ref(false);
const toggleMenu = () => {
clicked.value = !clicked.value;
};
return { clicked, toggleMenu };
},
};
</script>
NavBar.vue
<template>
<nav >
/* MORE CODE */
<div @click="toggleEvent">
<div></div>
<div></div>
<div></div>
</div>
</nav>
</template>
<script setup>
import { defineEmits, defineProps } from "vue";
const props = defineProps({
clicked: Boolean,
});
const emit = defineEmits(["toggleDrawer"]);
const toggleEvent = () => {
console.log("toggleEvent running");
emit("toggleDrawer", !props.clicked);
};
</script>
Backdrop.vue
<template v-if="props.clicked">
<div ></div>
</template>
<script setup>
import { defineProps } from "vue";
// eslint-disable-next-line no-unused-vars
const props = defineProps({
clicked: Boolean,
});
</script>
SideDrawer.vue
<template v-if="props.clicked">
<div ></div>
</template>
<script setup>
import { defineProps } from "vue";
const props = defineProps({
clicked: Boolean,
});
</script>
Am I passing in the prop wrong? Does "props.clicked" not work in "v-if"'s or templates? How should I implement the "v-if" with the "clicked" property I have?
CodePudding user response:
Your toggleDrawer
will auto-converted into kebab case when you use it in the parent component. So In app.vue you have to use it like @toggle-drawer
, Vue recommends to use kebab-cased event listeners in templates.
<NavBar :clicked="clicked" @toggle-drawer="toggleMenu()" />
From vue doc link
event names provide an automatic case transformation. Notice we emitted a camelCase event, but can listen for it using a kebab-cased listener in the parent. As with props casing, we recommend using kebab-cased event listeners in templates.
CodePudding user response:
After running the code, it is working fine. I have a few feedbacks to remove unnecessary code which is creating confusion and then you can see its working.
- Because props are immutable (read-only) in the child component that means their value will not be changed so there is no point to pass props value back (by doing
emit("toggleDrawer", !props.clicked)
) to the parent because the parent already has their original status. - Another point is, you are passing the data (props data) from the event by doing
emit("toggleDrawer", !props.clicked)
but not using it when calling the function@toggleDrawer="toggleMenu()"
inApp.vue
, so better to remove this data passing code. - The
clicked
property is updating in the parent (App.vue
) as well as inside the child components. Just console and print theclicked
property in the child and parent template like{{ clicked }}
at the top and you can see the updated status-
const toggleMenu = () => {
console.log('Before______', clicked.value)
clicked.value = !clicked.value;
console.log('After______', clicked.value)
};