1

We are looking to have a given path display different views depending on whether a user is authenticated, using vue-router

Two approaches I am looking at, but I am not sure how to make work at this point:

  • In the component() function return one of the two views, though function takes no arguments, so I am not sure how to get the environment context
  • Have two matching routes, with the first one having the second one as fallback, using logic added to router.beforeEach()

For the first approach:

  {
    path: '/mypath/',
    component: () => {
      if (condition) {
       import('src/pages/offers/PublicContent.vue');
      } else {
       import('src/pages/offers/PrivateContent.vue');
      }
    }
  }

For the second approach:

  {
    path: '/mypath/',
    component: () => {
      import('src/pages/offers/PrivateContent.vue');
    },
    meta: {
     requiresAuth: true
    }
  },
  {
    path: '/mypath/',
    component: () => {
      import('src/pages/offers/PublicContent.vue');
    }
  }

For the second approach I would think the router.beforeEach() would need to act as if the route didn't match, but I am not sure how to do that in a way that vue-router tries an alternative route match?

Andre M
  • 6,649
  • 7
  • 52
  • 93
  • 2
    third idea would be to just import both components, and [conditionally render](https://vuejs.org/guide/essentials/conditional.html) them based on the authentication state – yoduh Mar 16 '23 at 20:31
  • Can you present the suggestion as an answer, with an example, since this seems to be coding things at the component level, rather than in the routing logic. – Andre M Mar 17 '23 at 05:52

1 Answers1

1

After some experimenting I went with the second of the options, using a duplicate path and naming them. The logic in the beforeEach() handler was able to do the necessary magic.

The routes:

  {
    name: 'PrivateContent',
    path: '/mypath/',
    component: () => {
      import('src/pages/offers/PrivateContent.vue');
    },
    meta: {
     requiresAuth: true,
     publicAlt: 'PublicContent'
    }
  },
  {
    name: 'PublicContent',
    path: '/mypath/',
    component: () => {
      import('src/pages/offers/PublicContent.vue');
    }
  }

The beforeEach():

  router.beforeEach(
    async (
      to: RouteLocationNormalized,
      _from: RouteLocationNormalizedLoaded,
      next: (route?: { name: string } | string) => void
    ) => {
      // if authentication is not required then just display
      if (!isAuthRequired(to)) {
        next();
        return;
      }

      // handle private pages with public variants
      if (isAuthRequired(to) && !isAuthenticated() && publicFallback(to)) {
        if (to.meta && to.meta.publicAlt) {
          next({ name: to.meta.publicAlt as string });
        } else {
          next({ name: 'Error404' });
        }
        return;
      }
    }
  );

So here, if the page requires authentication, but were are not logged in and there is a public alternative for it, then redirect to it, otherwise display the 404 page. We could have displayed a 503, but 404 matches our design requirements.

Andre M
  • 6,649
  • 7
  • 52
  • 93
  • Not sure but Isn't this a very lengthy approach? If you consider @yoduh's comment, it would be much shorter and easier. What say? – Neha Soni Mar 17 '23 at 05:25
  • Without seeing their suggestion as an answer, I can't say. – Andre M Mar 17 '23 at 05:54
  • 1
    As I agree with @youduh's comment, I guess he meant, to create a common component, let's say, `Common.vue`, and render this component on your `/mypath` route. Inside the `Common` component, dynamic import the `Private` and `Public` component as per auth condition and render them in the template by using the component `:is` directive. You can see the dynamic import in this answer- https://stackoverflow.com/a/75541968/11834856. Please let me know if I am not clear. – Neha Soni Mar 17 '23 at 06:06
  • This doesn't really work for us, since each of the pages aren't sharing the same parent layout. – Andre M Mar 30 '23 at 18:33