Ionic 5/6 with Vue: Pages do not change sometimes (URL does)

Hi there,
I’m currently working on my first ionic project as for my bachelor thesis and have quite some issues with changing the pages.

Here is what I do in my app:

  1. Open the app, I got a field to enter a code to join a session
  2. I enter the code and get my desired content
  3. I’m navigating through the app, click some elements, switch views etc.
  4. I leave the session
  5. Now, sometimes (not always), when I re-enter the code, I can see that the URL changes and I also debugged the vue-router:
  • I go through beforeEnter
  • I go through beforeResolve
  • I reach onIonViewWillEnter and
  • I even reach onIonViewDidEnter

This must mean that the component is loaded, but not visible, right? What am I doing wrong, when it works on a fresh app, but not after some while?
I am using IonRouter for all navigations, with router.push in my own methods and I use the TabMenu with having three different Tab components - maybe it’s the tab that does not switch?

Also, I had the Issue with Ionic 5, but switched to 6 yesterday and nothing changed.

Let me know if there is any other information that could help!

It sounds like you might not have an IonPage on every page. That is required for Ionic to transition/route correctly. Also are you using any URL params? If so, are those changing but the content is not updating?

The URL is changing from /tabs/join to /participate/[ENTEREDCODE]
I am using the IonPage and IonContent on any view, but (of course?) not in the components that I’m using within and I also imported them in all the views. I also get no errors in the console or warnings. Here are the two that I want to route between:

From:

<template>
	<ion-page>
		<ion-content :fullscreen="true">
			<ion-grid>
				...
			</ion-grid>
		</ion-content>
	</ion-page>
</template>

export default {
	name: "ParticipateView",
	components: {IonContent, IonPage, IonGrid, IonRow, IonCol, IonItem, IonInput, IonLabel, IonButton, IonIcon},

To:

<template>
	<ion-page>
		<ion-content :fullscreen="true">
			<ion-refresher slot="fixed" @ionRefresh="doRefresh($event)">
				...
			</ion-refresher>
			<ion-grid>
				...
			</ion-grid>
		</ion-content>
	</ion-page>
</template>

export default {
	name: "ParticipateOverviewView",
	components: {IonPage, IonContent, IonGrid, IonRow, IonCol, IonList, IonItem, IonLabel, IonButton, IonIcon, IonRefresher, IonRefresherContent},
[...]

And the Router is like that:

const routes: Array<RouteRecordRaw> = [
	{
		path: '/',
		redirect: '/tabs/join'
	},
	{
		path: '/tabs/',
		name: 'public',
		component: PublicTabs,
		children: [
			{
				path: '',
				redirect: '/tabs/join'
			},
			{
				path: 'join',
				name: 'join',
				component: () => import('@/views/participate/ParticipateView.vue'),
				beforeEnter: (to, from, next) => {
					console.debug('before enter Join');
					next();
				}
			},
		]
	},
	{
		path: '/participate',
		name: 'participate',
		component: ParticipationTabs,
		children: [
			{
				path: ':code',
				name: 'participateOverview',
				component: () => import('@/views/participate/ParticipateOverviewView.vue'),
				beforeEnter: (to, from, next) => {
					console.debug('before enter POV');
					next();
				}
			},
		]
	}
]

const router = createRouter({
	history: createWebHistory(process.env.BASE_URL),
	routes
})

export default router

Here is a screenshot. You can see the new URL and that the last logs came from my OverviewPage

You don’t have a URL param in your /participate route for the code. It should be /participate/:code. Can you try adding that and see if it works?

I do, it’s in the child route. Participate is only the tab container.

However, after trying a lot and google around, I think it might be this bug: bug: router.replace eventually breaks routing · Issue #24432 · ionic-team/ionic-framework · GitHub

I’ve replaced all my router.back and router.replace with router.push, which is not ideal, but it seems to solve the problem. Just hoping they will release a fix soon.

Thanks for your help! :slight_smile:

Ah, sorry, I missed that. Ionic recommends not using children routes outside of tabs. You should use shared URLs.

We strongly caution against using nested routing in contexts other than tabs as it can quickly make navigating your app confusing.

/participate
/participate/:code

Yeah, and I am not using it outside of tabs; all my routes are child routes of one of my tabs. I also do not use further child routes but silblings in my routes, so this is just like it should be. See the example:

 {
    path: '/tabs/',
    component: Tabs,
    children: [
      {
        path: '',
        redirect: 'tab1',
      },
      {
        path: 'tab1',
        component: () => import('@/views/Tab1.vue'),
      },
      {
        path: 'tab2',
        component: () => import('@/views/Tab2.vue'),
      },
      {
        path: 'tab3',
        component: () => import('@/views/Tab3.vue'),
      },
    ],
  },

and mine is:

{
	path: '/participate',
	component: ParticipationTabs,
	children: [
		{
			path: ':code',
			component: () => import('@/views/participate/ParticipateOverviewView.vue'),
		},
	]
}

This looks good to me? :slight_smile: I really think it’s a bug now…

It very well could be related to the bug/issue you mentioned but want to make sure there isn’t something else going on :slight_smile:

In your original router code you shared, you have tab routes with children and then the participate route with children that is not within tabs. That is what I was referring to.

Should be something like this:

const routes: Array<RouteRecordRaw> = [
    {
        path: '/',
        redirect: '/tabs/join'
    },
    {
        path: '/tabs/',
        name: 'public',
        component: PublicTabs,
        children: [
            {
                path: '',
                redirect: '/tabs/join'
            },
            {
                path: 'join',
                name: 'join',
                component: () => import('@/views/participate/ParticipateView.vue'),
                beforeEnter: (to, from, next) => {
                    console.debug('before enter Join');
                    next();
                }
            },
        ]
    },
    {
        path: '/participate',
        name: 'participate',
        component: ParticipationTabs,
    },
    {
        path: '/participate/:code',
        name: 'participateOverview',
        component: () => import('@/views/participate/ParticipateOverviewView.vue'),
        beforeEnter: (to, from, next) => {
            console.debug('before enter POV');
            next();
        }
    },
]