Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to load different views for one route depending on user role?

Is there some implementation if I want to use the main page path '/' for different views depending on roles? Like if you are an authorized user you will see the MainAuth, if you are a guest you will see the MainGuest? Here is some code that would help me to explain.

const routes = [
  {
    path: '/',
    name: 'main_auth',
    component: MainAuth,
    meta: {
      requiresAuth: true
    }
  },
  {
    path: '/',
    name: 'main_guest',
    component: MainGuest,
    meta: {
      requiresGuest: true
    }
  }
]

For this example, it just loads the first path I declared whatever I am a guest or user. To be more specific, I have a login/register module on the main path '/' and I want to load an authorized user page on the same path '/' after login.

like image 915
fitzima Avatar asked Mar 31 '20 21:03

fitzima


2 Answers

Here are 3 options:

1. Simple Computed View

To allow a single route to display any number of conditional views, use a computed component.

Register views:

import Vue from 'vue'

import ViewDefault from '/views/Default.vue'
Vue.component('view-default', ViewDefault )

import ViewAuth from '/views/Auth.vue'
Vue.component('view-auth', ViewAuth )

import ViewGuest from '/views/Guest.vue'
Vue.component('view-guest', ViewGuest)

Route:

  {
    path: '/',
    name: 'index',
    component: ViewDefault
  },

View Default.vue

<template>
  <component :is="view"></component>
</template>

<script>

export default {

  name: 'view-default',

  computed: {

   view() {
     return this.isAuthenticated ? 'view-auth' : 'view-guest'
   }  

  }

}

</script>

You will be able to create as many conditional checks you want in view().


2. Conditional Named Views ( Concept not tried )

I would consider experimenting with the use of named views, I believe you could double view many paths with user and guest versions, not something I tried yet:

https://router.vuejs.org/guide/essentials/named-views.html

<template>

  <div>

    <template v-if="isAuthenticated">
      <router-view name="authed"/>
    </template>
  
    <template v-else>
      <router-view/>
    </template>

  </div>

</template>

Router


const router = new VueRouter({
  routes: [
    {
      path: '/',
      components: {
        default: MainGuest,
        authed: MainAuth,
      }
    }
  ]
})


3. Computed Layouts

However ... I personally use I/O blocking layouts, computed layout.

I check various application states that control what layouts are displayed depending on the conditions before a final layout is computed that would contain <router-view>.

I let the router paths specify custom layouts via meta data, set alternate layout else falls back to default layout.

Router

  {
    name: 'index',
    path: '/',
    meta: {
      title: 'Welcome',
      guard: 'auth'
    },
    component: import('@/views/welcome')
  },

  {
    name: 'settings',
    path: '/settings',
    meta: {
      title: 'Account Settings',
      layout: 'settings',
      guard: 'auth'
    },
    component:  import('@/views/account-settings')
  }

App.vue

<template>

  <div id="app" v-cloak :class="$root.layout">

    <transition name="component-fade" mode="out-in">
      <component :is="$root.layout"/>
    </transition>

  </div>

</template>

main.js


import Vue from 'vue'
import router from './router'
import store from './store'
import '@/views/layouts'
import App from './App'


// Use Vuex to store states, `axios` calls and response `interceptors` are used to `commit` computed mapped state conditions globally.

import { mapState, mapGetters } from 'vuex'

Vue.mixin({

  computed: {

    ...mapState(['user', 'isAuthenticating', 'isOffline']),

    ...mapGetters(['isAuthenticated']),

  }

})

new Vue({

  el: '#app'
  render: h => h(App),
  store,
  router,

  computed: {

    layout () { // could move this to vuex and access via mapGetters...

      if (this.isOffline) {
        return 'layout-offline'
      }

      if (this.isAuthenticating) {
        return 'layout-authenticating'
      }

      if (this.$route.meta.hasOwnProperty('guard')) {

        if(this.$route.meta.guard === 'auth' && !this.isAuthenticated) {
          return 'layout-login'
        }

      }

      // If current route has a preferred layout use it else fallback to `layout-default`
      let layout = this.$route.meta.layout || 'default'

      return `layout-${layout}`
    }

  },

})


views/layouts/index.js

import Vue from 'vue'

import LayoutOffline from './layout-offline')
import LayoutAuthenticating from './layout-authenticating')
import LayoutLogin from './layout-login')

import LayoutDefault from './layout-default')
import LayoutSettings from './layout-settings')

Vue.component('layout-offline', LayoutOffline)
Vue.component('layout-authenticating', LayoutAuthenticating)
Vue.component('layout-login', LayoutLogin)

Vue.component('layout-default', LayoutDefault)
Vue.component('layout-settings', Layoutsettings)

Only layout-default or any router path specifying a custom layout should contain <router-view> along with header, footer, sidebar etc...

You could mix this up with name views, different computed layouts could display a different named router-view for the same path.

like image 121
Marc Avatar answered Oct 18 '22 18:10

Marc


Since you want to use only one path, you could use a basic route and do the work in the component itself. The route:

const routes = [
  {
    path: '/',
    name: 'main',
    component: Main,
  }
]

Main view component:

<template>
  <div>
    <Auth v-if="!loggedIn" />
    <MainContent v-else />
  </div>
</template>

Auth component:

<template>
  <div>
    <Login v-if="showLogin" />
    <Register v-else />
  </div>
</template>

With this, you check if the user is logged in from the Main view component and show either the Auth component or the main content.

Then, in the Auth component, you use a toggle that allows the user to switch between a Login and Register component.

like image 26
Dan Avatar answered Oct 18 '22 18:10

Dan