[Vuejs]-Ionic-vue menu breaks after navigating to another page

0👍

This is a tricky bug in Ionic. It’s been around for a long time and they still haven’t solved it. See this GitHub issue for more details.

However, they don’t provide a resolution that works for everyone, and no solution for Vue. I’ve had some issues with IonMenu as well, so I figured I’d take a crack at the problem.

I modified your code, and it works in my environment. The gist of it is that the menu remains disabled after switching routes, and it is complicated by animations. I added a computed disabled and onBeforeRouteLeave handlers. I only had success with both in play. This required menus to have a menu-id, and instead of making a bunch of props, I just used provide(). My changes are below.

Dashboard.vue

<template>
  <IonPage>
    <Header />

    <IonContent :fullscreen="true">
      <span>Dashboard</span>
    </IonContent>
  </IonPage>
</template>

<script setup lang="ts">
import {IonContent, IonPage, menuController} from "@ionic/vue";
import Header from "../components/Header.vue";
import {onBeforeRouteLeave} from "vue-router";
import {provide} from "vue";

const menuId = "dashboard-menu"
provide("menu-id", menuId)
provide("route-name", "dashboard")

onBeforeRouteLeave(async () => {
  await menuController.close(menuId)
  await menuController.enable(true, menuId)
})

</script>

Profile.vue

<template>
  <IonPage>
    <Header />
    <IonContent :fullscreen="true">
      <span>Profile Page</span>
    </IonContent>
  </IonPage>
</template>

<script setup lang="ts">
import {IonContent, IonPage, menuController} from "@ionic/vue";
import Header from "../components/Header.vue";
import {onBeforeRouteLeave} from "vue-router";
import {provide} from "vue";

const menuId = "profile-menu"
provide("menu-id", menuId)
provide("route-name", "profile")

onBeforeRouteLeave(async () => {
  await menuController.close(menuId)
  await menuController.enable(true, menuId)
})

</script>

<style>
ion-modal {
  --height: auto;
}
</style>

HeaderMenu.vue

<template>
  <IonMenu side="end" :menu-id="menuId" content-id="main-content" :disabled="disabled">
    <IonHeader>
      <IonToolbar color="primary"></IonToolbar>
    </IonHeader>
    <IonContent>
      <IonItemGroup>
        <IonList class="ion-no-padding">
          <IonMenuToggle>
            <IonItem class="ion-text-center">
              <IonButton
                expand="full"
                fill="clear"
                size="small"
                @click="navigate('dashboard')"
              >
                <span>Dashboard</span>
              </IonButton>
            </IonItem>
            <IonItem class="ion-text-center">
              <IonButton
                expand="full"
                fill="clear"
                size="small"
                @click="navigate('profile')"
              >
                <span>Profile</span>
              </IonButton>
            </IonItem>
          </IonMenuToggle>
        </IonList>
      </IonItemGroup>
    </IonContent>
  </IonMenu>
</template>

<script setup lang="ts">
import {
  IonHeader,
  IonToolbar,
  IonMenu,
  IonMenuToggle,
  IonContent,
  IonButton,
  IonList,
  IonItem,
  IonItemGroup,
} from "@ionic/vue";
import {computed, inject} from "vue";
import {useRouter} from "vue-router";

const routeName = inject("route-name")
const menuId: string = inject("menu-id")!
const router = useRouter()

const disabled = computed(() => {
  return router.currentRoute.value.name !== routeName
})

const navigate = (routeName: string) => {
  router.push({ name: routeName });
};
</script>

<style>
/* This fixes the issue with the menu not being clickable */
.menu-content-open {
  pointer-events: unset !important;
}
</style>

0👍

I have see your code in HeaderMenu.vue page in IonToolbar component in set
your url and in defaultHref also set daynamic and static url(router).

ex:-

<ion-buttons slot="start">
     <ion-back-button defaultHref="/" />
</ion-buttons>

0👍

Header being a component common to both of your pages, standard practice would be to move it outside of both and have <Header /> exist as a sibling just above <ion-router-outlet /> in App.vue

In order to not have the header and page content overlap each other, the router component needs to be wrapped with <IonContent>

<ion-app>
  <Header />
  <IonContent>
    <ion-router-outlet id="main-content" />
  </IonContent>
</ion-app>

Doing just these two things fixes your issue.

Updated codesandbox

Leave a comment