I have a Vue3 project with vue-router and headless UI. In my Navbar component i want to have a dropDownMenu. For the dropDownMenu I came across headless UI Menu Component https://headlessui.com/vue/menu.
In the headlessUI example they use <a>
tags for the links, which do work as intended. The Menu does indeed close after click. If I use instead <RouterLink>...</RouterLink>
I will be redirected to the related path and View, but the Menu does not close.
I tried using the close() slot prop as they show here https://headlessui.com/vue/menu#closing-menus-manually but it still do not work.
<RouterLink>
?<a>
instead RouterLink
? (As I understood <a>
should be used for external navigation like to other pages(youtube etc) and vue-router for internal pagesBelow my code: Navbar:
<template >
<header class="sticky top-0 shadow-lg bg-navbar" v-show="loggedIn">
<nav class="container flex flex-col sm:flex-row items-center gap-4 ">
<div class="px-2 pt-2 sm:flex">
<RouterLink :to="{name:'profile'}">
<p class=" mt-1 block px-2 py-1 font-semibold rounded hover:bg-navbar-buttons " >Meine Medien</p>
</RouterLink>
<RouterLink v-if="showLoanBoard" :to="{name:'ausleihe'}">
<p class="mt-1 block px-2 py-1 font-semibold rounded hover:bg-navbar-buttons">Ausleihe/Rückname</p>
</RouterLink>
<InvetoryDropDown v-if="showInventoryDropDown" />
<InventoryMenu v-if="showInventoryDropDown"/>
<InventoryMenuCopy v-if="showInventoryDropDown"/>
<AdminDropDownVue v-if="showAdminDropDown" />
</div>
<div>
<button
class=""
@click="logout">Logout</button>
</div>
</nav>
</header>
</template>
<script setup>
import AdminDropDownVue from './AdminDropDown.vue';
import InvetoryDropDown from './InventoryDropDown.vue';
import InventoryMenu from './InventoryMenu.vue';
import InventoryMenuCopy from './InventoryMenuCopy.vue';
import { computed, ref } from 'vue';
import { useStore } from 'vuex';
import { useRouter } from 'vue-router';
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
const store = useStore();
const router = useRouter();
const loggedIn = computed(() =>{
return store.state.auth.status.loggedIn;
})
//A computed ref! Access with .value
const currentUser = computed(() =>{
return store.state.auth.user;
})
const showAdminDropDown = computed(() =>{
if(currentUser.value && currentUser.value.roles){
return currentUser.value['roles'].includes('ADMIN')
}
})
const showLoanBoard = computed(() =>{
if(currentUser && currentUser.value.roles){
return currentUser.value['roles'].includes('LIBRARIAN')
||currentUser.value['roles'].includes('ADMIN')
||currentUser.value['roles'].includes('LOAN_HELPER')
}
})
const showInventoryDropDown = computed(() =>{
if(currentUser && currentUser.value.roles){
return currentUser.value['roles'].includes('LIBRARIAN')
|| currentUser.value['roles'].includes('ADMIN')
|| currentUser.value['roles'].includes('INVENTORY_HELPER')
}
})
const logout = () => {
store.dispatch('auth/logout');
router.push('/login')
}
</script>
Menu with <a>
<template>
<Menu as="div" class="relative inline-block text-left">
<div>
<MenuButton class="inline-flex w-full justify-center gap-x-1.5 rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50">
A href
<ChevronDownIcon class="-mr-1 h-5 w-5 text-gray-400" aria-hidden="true" />
</MenuButton>
</div>
<transition enter-active-class="transition ease-out duration-100" enter-from-class="transform opacity-0 scale-95" enter-to-class="transform opacity-100 scale-100" leave-active-class="transition ease-in duration-75" leave-from-class="transform opacity-100 scale-100" leave-to-class="transform opacity-0 scale-95">
<MenuItems class="absolute right-0 z-10 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
<div class="py-1">
<MenuItem v-slot="{ active }">
<a href="/inventory" :class="[active ? 'bg-gray-100 text-gray-900' : 'text-gray-700', 'block px-4 py-2 text-sm']">Übersicht</a>
</MenuItem>
<MenuItem v-slot="{ active }">
<a href="/inventory/add" :class="[active ? 'bg-gray-100 text-gray-900' : 'text-gray-700', 'block px-4 py-2 text-sm']">Aufnehmen</a>
</MenuItem>
<MenuItem v-slot="{ active }">
<a href="/inventory/delete" :class="[active ? 'bg-gray-100 text-gray-900' : 'text-gray-700', 'block px-4 py-2 text-sm']">Löschen</a>
</MenuItem>
</div>
</MenuItems>
</transition>
</Menu>
</template>
<script setup>
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'
import { ChevronDownIcon } from '@heroicons/vue/20/solid'
</script>
Menu with RouterLink
, which do not close on click
<template>
<Menu as="div" class="relative inline-block text-left">
<div>
<MenuButton class="inline-flex w-full justify-center gap-x-1.5 rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50">
RouteLinks
<ChevronDownIcon class="-mr-1 h-5 w-5 text-gray-400" aria-hidden="true" />
</MenuButton>
</div>
<transition enter-active-class="transition ease-out duration-100" enter-from-class="transform opacity-0 scale-95" enter-to-class="transform opacity-100 scale-100" leave-active-class="transition ease-in duration-75" leave-from-class="transform opacity-100 scale-100" leave-to-class="transform opacity-0 scale-95">
<MenuItems class="absolute right-0 z-10 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
<div class="py-1">
<MenuItem v-slot="{ close }">
<RouterLink
:class="[close ? 'bg-gray-100 text-gray-900' : 'text-gray-700', 'block px-4 py-2 text-sm']" to="/inventory"
@click="close">
Übersicht
</RouterLink>
</MenuItem>
<MenuItem v-slot="{ close }">
<RouterLink @click="close" :class="[close ? 'bg-gray-100 text-gray-900' : 'text-gray-700', 'block px-4 py-2 text-sm']" to="/inventory/add">Aufnehmen</RouterLink>
</MenuItem>
</div>
</MenuItems>
</transition>
</Menu>
</template>
<script setup>
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'
import { ChevronDownIcon } from '@heroicons/vue/20/solid'
</script>
The @click listener in your RouterLink doesn't work. I'm not sure it's a pretty solution but I resolved it like this:
<MenuItem v-for="item in userNavigation" :key="item.name" v-slot="{ active }">
<a @click="routerLinkMethod(item.href)">
{{ item.name }}
</a>
</MenuItem>
In the methods:
routerLinkMethod(href) {
this.$router.push(href);
},