Display layout based on boolean from Vuex - javascript

I got Vue2 app with vue-router with routings configured like that:
export default {
path: "/",
redirect: "/dashboard",
component: AdminLayout,
meta: {
requiresAuth: true
},
children: [
{
path: "/dashboard",
name: "Dashboard",
component: Dashboard
},
{
path: "/add/user",
name: "InviteUser",
component: InviteUser
},
{
path: "/groups",
name: "Groups",
component: Groups
},
...
In app, we got two different types of users - admin and normal user. Some of those routings should be accessible for both, but the problem is that user should see different layout base on its type (permission) - AdminLayout for admins and UserLayout for normal users.
Is there any way to show app which template should user see based on boolean from vuex with keeping route path?
on /dashboard admin will see dashboard with AdminLayout
on /dashboard normal user will see dashboard with UserLayout
My main routing cofig:
import Vue from "vue";
import VueRouter from "vue-router";
import SessionRoutes from "./session.js";
import AdminRoutes from "./admin.js";
import defaultRoutes from "./default";
Vue.use(VueRouter);
const routes = [AdminRoutes, SessionRoutes, defaultRoutes];
const router = new VueRouter({
mode: "history",
routes
});
export default router;

you can set a condition in the layout part on the current page for example for when you use nuxt:
<script>
export default {
layout: (ctx) => (ctx.isAdmin ? 'adminLayout' : 'userLayout'),
}
</script>
but I opine you don't use Nuxt.js and I think below solution is suitable for your question:
Using the meta-object in our route
set dynamic component on app.vue page
code for about.vue page
import Home from '../views/Home.vue'
import About from '../views/About.vue'
import LayoutA from '../layouts/LayoutA.vue'
import LayoutB from '../layouts/LayoutB.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home,
meta: { layout: LayoutA }
},
{
path: '/about',
name: 'About',
component: About,
meta: { layout: LayoutB }
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router;
<!--app.vue page -->
<template>
<div id="app">
<component :is="this.$route.meta.layout || 'div'">
<router-view />
</component>
</div>
</template>
<script>
export default {
name: "App",
};
</script>
<!--about page-->
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
<script>
export default {
name: "About"
};
</script>

To get a different layout working for the dashboard and all child routes your layouts have to use the router view component. I use a wrapper component to handle this in one of my projects following this approach.
Excerpt from my router config:
…
{
component: MainFrame,
name: 'main',
path: '/',
redirect: 'dashboard',
children: [
{
alias: 'dashboard',
component: Dashboard,
name: 'dashboard',
path: '', // Default child route → be equivalent to /dashboard
},
{
path: "add/user",
name: "InviteUser",
component: InviteUser
},
{
path: "groups",
name: "Groups",
component: Groups
},
],
},
…
In your MainFrame component you can wrap your layout in a dynamic component. So you can switch your layout easely based on a Vuex getter:
MainFrame.vue
<template>
<component :is="layout">
<!-- you can use slots too if you like -->
</component>
</template>
<script>
import AdminLayout from './AdminLayout'
import UserLayout from './UserLayout'
export default {
computed: {
layout() {
return this.$store.getters['isAdminUser'] ? AdminLayout : UserLayout
}
}
}
</script>
And your layouts have to use <router-view />. It works as a wrapper for all nested routes and therefore you have only one place to handle the layout for the dashboard and child pages:
AdminLayout.vue
<template>
<div class="auth-view">
<header>…</header>
<aside>…</aside>
<main>
<transition name="fade" mode="out-in">
<router-view />
</transition>
</main>
<footer>…</footer>
</div>
</template>
UserLayout.vue
<template>
<div class="guest-view">
<router-view />
</div>
</template>
EDIT:
This approach can be used for deeper nesting too. Assuming your user route should have another child routes you can solve this on the router config using a <router-view /> within a render function:
…
// Let's split down user related routes
{
component: { render: (h) => h('router-view') },
name: 'usersView',
path: 'users',
redirect: 'ListUsers',
children: [
// Show users
{
path: '', // route: /users/
name: 'ListUsers',
component: ListUsers
},
// Show a single user
{
path: ':id', // route: /users/{userId}
name: 'ViewUser',
component: ViewUser
},
// Invite new users
{
path: 'add', // route: /users/add
name: 'InviteUser',
component: InviteUser
},
]
}
…

Related

VueJs Router: App.vue Component shows up on every page

I'm working with currently with the Vue Router, but when I write something in App.vue it shows up on every page, why?
The app.vue will always render since it's the main component. To display a content specific to the page, you should use <router-view />. It will render the component you define on your route file. Here is the example
import { createRouter, createWebHistory } from 'vue-router'
import Login from '../views/Auth/Login.vue'
import Register from '../views/Auth/Register.vue'
import LandingPage from '../views/LandingPage.vue'
const routes = [
{
path: '/',
name: 'LandingPage',
component: LandingPage
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/register',
name: 'Register',
component: Register
},
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router

Vue js Routers how to open a page when following a specific link

I am studying Vue JS and I have problems with routers, I want certain content to open when writing on the page address for example "Home", and more precisely, I have a component called HomePage, I want to open this component when writing in url " Home "for example "http://localhost:8080/Home" and by default, if nothing is specified in the address, then an empty page would open
App.vue
<template>
<HomePage></HomePage>
</template>
<script>
import HomePage from "#/View/HomePage/HomePage";
export default {
name: 'App',
components: {
HomePage
}
}
</script>
Edit
App.vue
<template>
<HomePage>
<router-view />
</HomePage>
</template>
<script>
import HomePage from "#/View/HomePage/HomePage";
export default {
name: 'App',
components: {
HomePage,
}
}
</script>
Index.js
import Vue from 'vue'
import Router from 'vue-router'
import HomeRoute from '#/components/routes/HomeRoute'
import Rar from "./src/View/Rar";
Vue.use(Router)
const router = new Router({
routes: [
{
path: '/home',
name: 'HomeRoute',
alias: '*',
component: HomeRoute
},
{
path: '/Rar',
name: 'Rar',
component: Rar
}
]
})
export default router
You should specify a path inside your routes.
Steps
Your main app.vue should look like this
<div>
<navbar />
<router-view />
<footer />
</div>
</template>
make a folder router and inside an index.js (or more structured files)
It must look like this
import Vue from 'vue'
import Router from 'vue-router'
import HomeRoute from '#/components/routes/HomeRoute'
import AboutRoute from '#/components/routes/AboutRoute'
Vue.use(Router)
const router = new Router({
routes: [
{
path: '/home',
name: 'HomeRoute',
alias: '*',
component: HomeRoute
},
{
path: '/about',
name: 'About',
component: AboutRoute
}
]
})
export default router
The first path is the '/home' with alias *.
if you type '/home' in the url you will go to the main page.
Also the alias: '*' means that everything you type after will redirect you to this route, unless it finds another route registered
You are missing some code from main.js.
I cannot see Vue getting initalized anywhere.
You should import router in your main.js and use .use(router) in there
App.vue
<template>
<router-view />
</template>
/router/index.js
import Router from 'vue-router'
import HomeRoute from '#/components/routes/HomeRoute.vue'
const router = new Router({
routes: [
{
path: '/home',
component: HomeRoute
}
]
})
export default router
main.js
import Vue from 'vue'
import App from "./App.vue";
import router from '#/router'
const app = Vue.createApp(App)
app.use(router)
app.mount('#app')
You can check your HomePage component. Does it has slot that you put router-view there? What if you use simple router-view without HomePage? Did you register router in your main app.js?
And if I correctly understand you, you can do something like this to manage your routes in index.js
const router = new Router({
routes: [
{
path: '/',
beforeEnter: (from, to, next) => next({path: '/home'})
},
{
path: '/home',
name: 'HomeRoute',
component: HomeRoute
}
]
})
Read about VueRouter hooks https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards
Also you can use children:
import HomeLayout from '#/layouts/HomeLayout.vue'
import HomePage from '#/views/HomePage.vue'
import HomeAbout from '#/views/HomePage.vue'
const router = new Router({
routes: [
{
path: '/',
beforeEnter: (from, to, next) => next({path: '/home'})
},
{
path: '/home',
component: HomeLayout,
children: [
// full url will be like: localhost:8080/home
{
path: '',
name: 'Home',
component: HomePage
},
// full url will be like: localhost:8080/home/about
{
path: 'about',
name: 'HomeAbout',
component: HomeAbout
}
]
}
]
})

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.
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.
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.
You can redirect the user to another route using the beforeEach method.
Example :
router.beforeEach((to, from, next) => {
// Determine if the route requires Authentication
const requiresAuth = to.matched.some(x => x.meta.requiresAuth);
const user = auth.getUser();
// If it does and they are not logged in, send the user to "/login"
if (requiresAuth && !user) {
next("/login");
} else {
// Else let them go to their next destination
next();
}
});

Vue nested routes not displaying their components and no errors

When I access URL profile/admin, I see the profile component, however, if I try URL profile/admin/locations or profile/admin/map, I don't load the Map or Locations components. Any ideas why?
The routes in question:
import Home from './components/Home.vue';
import Profile from './components/Profile.vue';
import Map from './components/Map.vue';
import Locations from './components/Locations.vue'
export const routes = [
{ path: '', component: Home, name: 'Home'},
{ path: '/profile/:username', component: Profile, name: 'Profile', chilren: [
{ path: '/profile/:username/locations', component: Locations, name: 'Locations'},
{ path: '/profile/:username/map', component: Map, name: 'Map'}
]},
];
Profile component template:
<template>
<div class="wrapper">
<p>Profile</p>
<router-view></router-view>
</div>
</template>
Location component template:
<template>
<div class="wrapper">
<p>Location</p>
</div>
</template>
Map component template:
<template>
<div class="wrapper">
<p>Location</p>
</div>
</template>
You have a typo in your routes - chilren instead of children.
You don't need to specify full path in children routes. Correct way of doing:
export const routes = [
{ path: '', component: Home, name: 'Home'},
{ path: '/profile/:username', component: Profile, name: 'Profile', children: [
{ path: 'locations', component: Locations, name: 'Locations'},
{ path: 'map', component: Map, name: 'Map'}
]},
];

VueJS named routes not working

I try to work with named routes in a Vue app.
Exactly the same setup works totally fine in an other Vue project.
If I click on a named router-link, the just disappears.
If I check the element in the browser, there is an empty comment at the place, where the section should be.
The console isn't showing any errors.
Has anyone seen something similar?
Thank's for every help!
Code:
index.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from '#/components/views/home';
import AskQuestion from '#/components/views/ask-question';
import AddQuestion from '#/components/views/add-question';
import CompleteQuestions from '#/components/views/complete-questions';
import Survey from '#/components/views/survey';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/ask-question',
name: 'AskQuestion',
component: AskQuestion
},
{
path: '/add-question',
name: 'AddQuestion',
component: AddQuestion
},
{
path: '/complete-questions/:surveyId',
name: 'CompleteQuestions',
component: CompleteQuestions
},
{
path: '/survey/:surveyId',
name: 'Survey',
component: Survey
}
]
});
HTML
<router-link :to="{name: 'survey', params: {surveyId: survey.id}}">
<p class="viewQuestions">View questions</p>
</router-link>
App.vue
<template>
<div id="app">
<navigation></navigation>
<router-view/>
</div>
</template>
<script>
import Navigation from '#/components/generel/navigation';
export default {
name: 'app',
components: {
Navigation
}
};
</script>
<style src="./assets/scss/_general.scss" lang="scss"></style>
in route config , set history mode and then go to your link and click that LINK , and then go check URL address bar
export default new VueRouter({
mode:'history',
......
});
for example if you click on this
<router-link :to="{ name: 'home' }"> Home </router-link>
and your rute is
{
path:'/home',
component:Home,
name:'home'
}
if your route workin well , you must see this url in browser address bar
https://localhost/home

Categories