How to add router with query param to router list? - vue.js

I want to add a route with query params.
If the url is blog, then navigate to index page.
If the url includes the author query param, replace a component on the page with the BlogAuthorPage component.
router: {
extendsRoutes(routes, resolve) {
routes.push({
name: 'author-page-detail',
path: '/blog?author=*',
component: resolve(__dirname, 'pages/blog/author-page.vue')
})
}
}

This should not be done in nuxt.config.js's router key but rather in your blog.vue page directly with a component router guard.
The code below should be enough to check if the route does have a author query params and redirect to the blog/author-page page.
<script>
export default {
beforeRouteEnter(to, from, next) {
next((vm) => {
if (vm.$route.query?.author) next({ name: 'blog-author-page' })
else next()
})
},
}
</script>

I use "#nuxtjs/router": "^1.6.1",
nuxt.config.js
/*
** #nuxtjs/router module config
*/
routerModule: {
keepDefaultRouter: true,
parsePages: true
}
router.js
import Vue from 'vue'
import Router from 'vue-router'
import BlogIndexPage from '~/pages/blog/index'
import BlogAuthorPage from '~/pages/blog/author-page';
Vue.use(Router);
export function createRouter(ssrContext, createDefaultRouter, routerOptions, config) {
const options = routerOptions ? routerOptions : createDefaultRouter(ssrContext, config).options
return new Router({
...options,
routes: [
...options.routes,
{
path: '/blog',
component: ssrContext.req.url.includes('/blog?author') ? BlogAuthorPage : BlogIndexPage
}
]
})
}

Related

Vue 3 dynamic components at router level

Dynamic imports is needed for me, eg. i have 10 layouts, but user only visited 3 layouts, I should not import all of the layouts, since its consumed unnecessary resources.
Since its dynamic import, each time i switch between Login & Register path <RouterLink :to"{name: 'Login'}" /> & <RouterLink :to"{name: 'Register'}" />, I got rerender or dynamic import the layout again.
My question is what is the better way to handle it, without rerender or dynamic import the layout again? Or can I save the dynamic import component into the current vue 3 context?
App.vue this is my app with watching the router and switch the layout based on route.meta.layout
<template>
<component :is="layout.component" />
</template>
<script>
import DefaultLayout from "./layout/default.vue";
import {
ref,
shallowRef,
reactive,
shallowReactive,
watch,
defineAsyncComponent,
} from "vue";
import { useRoute } from "vue-router";
export default {
name: "App",
setup(props, context) {
const layout = shallowRef(DefaultLayout);
const route = useRoute();
watch(
() => route.meta,
async (meta) => {
if (meta.layout) {
layout = defineAsyncComponent(() =>
import(`./layout/${meta.layout}.vue`)
);
} else {
layout = DefaultLayout;
}
},
{ immediate: true }
);
return { layout };
},
};
</script>
router/index.js this is my router with layout meta
import { createRouter, createWebHistory } from "vue-router";
import Home from "#/views/Home.vue";
import NotFound from "#/views/NotFound.vue";
const routes = [
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/login",
name: "Login",
meta: {
layout: "empty",
},
component: function () {
return import(/* webpackChunkName: "login" */ "../views/Login.vue");
},
},
{
path: "/register",
name: "Register",
meta: {
layout: "empty",
},
component: function () {
return import(/* webpackChunkName: "register" */ "../views/Register.vue");
},
},
{ path: "/:pathMatch(.*)", component: NotFound },
];
const router = createRouter({
history: createWebHistory(import.meta.env.VITE_GITLAB_BASE_PATH),
routes,
scrollBehavior(to, from, savedPosition) {
// always scroll to top
return { top: 0 };
},
});
export default router;
You could use AsyncComponent inside the components option and just use a computed property that returns the current layout, this will load only the current layout without the other ones :
components: {
layout1: defineAsyncComponent(() => import('./components/Layout1.vue')),
layout2: defineAsyncComponent(() => import('./components/Layout2.vue')),
},
Had this issue and Thorsten Lünborg of the Vue core team helped me out.
add the v-if condition and that should resolve it.
<component v-if="layout.name === $route.meta.layout" :is="layout">

Vue Router works fine in development, but doesn't match routes in production

I have a Vue SPA that's being served by an ASP Core API. When I run it in development mode, everything works perfectly. But as soon as I deploy it to production (on an Azure App Service), I always get a blank page.
It seems to be specifically the router that can't match the routes, as I can put some arbitrary HTML into my App.vue, and that will render.
If I go into the developer tools, I can see that the index.html and all .js files download successfully and there are no errors in the console. This is true no matter what URL I visit e.g. myapp.com and myapp.com/login, both download everything but nothing displays on screen.
I have seen several posts saying to change the routing mode to hash, but I still get the same result with that.
Please see below my files:
main.ts
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import vuetify from './plugins/vuetify';
import { LOGIN_INITIALISE } from './use-cases/user-auth/AuthModule';
Vue.config.productionTip = false;
store.dispatch(LOGIN_INITIALISE)
.then(() => {
new Vue({
router,
store,
vuetify,
render: (h) => h(App),
}).$mount('#app');
});
App.vue
<template>
<div>
<div>test</div>
<router-view></router-view>
</div>
</template>
<script lang="ts">
/* eslint-disable no-underscore-dangle */
import Vue from 'vue';
import Axios from 'axios';
import { LOGOUT } from './use-cases/user-auth/AuthModule';
import { LOGIN } from './router/route-names';
export default Vue.extend({
name: 'App',
created() {
// configure axios
Axios.defaults.baseURL = '/api';
Axios.interceptors.response.use(undefined, (err) => {
// log user out if token has expired
if (err.response.status === 401 && err.config && !err.config.__isRetryRequest) {
this.$store.dispatch(LOGOUT);
this.$router.push({ name: LOGIN });
}
throw err;
});
},
});
</script>
router/index.ts
import Vue from 'vue';
import {} from 'vuex';
import VueRouter, { RouteConfig } from 'vue-router';
import store from '#/store';
import {
HOME,
LOGIN,
SIGNUP,
USERS,
} from './route-names';
Vue.use(VueRouter);
const routes: Array<RouteConfig> = [
{
path: '/',
name: HOME,
component: () => import('#/views/Home.vue'),
},
{
path: '/login',
name: LOGIN,
component: () => import('#/views/Login.vue'),
},
{
path: '/signup',
name: SIGNUP,
component: () => import('#/views/SignUp.vue'),
},
{
path: '/users',
name: USERS,
component: () => import('#/views/Users.vue'),
beforeEnter: (to, from, next) => {
if (store.getters.userRole === 'Admin') {
next();
} else {
next({ name: HOME });
}
},
},
{
path: '*',
name: '404',
component: {
template: '<span>404 Not Found</span>',
},
},
];
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes,
});
router.beforeEach((to, from, next) => {
if (store.getters.isAuthenticated) {
next();
} else if (to.name === LOGIN || to.name === SIGNUP) {
next();
} else {
next({ name: LOGIN });
}
});
export default router;
Finally after completely rebuilding my router piece by piece, I found the issue. I found that the problem was in this global route guard:
router.beforeEach((to, from, next) => {
if (store.getters.isAuthenticated) {
next();
} else if (to.name === LOGIN || to.name === SIGNUP) {
next();
} else {
next({ name: LOGIN });
}
});
Specifically, the isAuthenticated getter was throwing an error (silently), so all of the routes were failing before they could render. I wrapped my isAuthenticated logic in a try-catch that returns false if an error is thrown, and now everything works fine.
I still don't understand why this only affects the production build, but hopefully this experience will be useful to others stuck in the same situation.

Redirect to specific url in case of wrong url in vuejs

I have two separate routing files where I am importing the component and defining their routing in each of its file and using it in index.js file. Here are my files code:
//router1.js
import Layout1 from 'Layouts/Panel.vue';
const Users = () => import('Views/Users.vue');
const Reports = () => import('Views/Reports.vue');
export default {
path: '/layout1',
component: Layout1,
redirect:'/layout1/reports',
children:[
{
path: 'reports',
component: Reports,
name:'Reports'
},
{
path: 'users',
component: Users,
name:'Users'
}
]
}
//router2.js
import Layout2 from 'Layout/Panel2';
const Demo1 = () => import('Views/Demo1');
const Demo2 = () => import('Views/Demo2');
export default {
path: '/',
component: Layout2,
redirect:'/demo1',
children:[
{
path: '/demo1',
component: Demo1
},
{
path: '/demo2',
component: Demo2
}
]
}
// index.js
import Vue from 'vue'
import Router from 'vue-router'
import router1 from './router1';
import router2 from './router2';
const NotFound = () => import('Views/NotFound.vue');
Vue.use(Router)
export default new Router({
mode: 'history',
routes: [
router1,
router2,
{
path: '*',
component: NotFound,
name:'NotFound',
},
]
})
Now, I want to redirect to specific url i.e "not-found" in case of wrong URL. In "NotFound" component I am adding below line of code in mounted lifecycle hook which redirects to URL "not-found".
this.$router.replace({ path: 'not-found' });
But if URL is having parameters or query string it will append to it. For e.g- http://localhost:8080/home/not-found
What I want is that it only shows http://localhost:8080/not-found How should I achieve this. Please help. Thanks!
try this in your mounted function. worked on my side.
this.$router.push({path: '/not-found'})

Vue Js Axios get method

I use vue.js and I try to set a parameter id in axios.get request and I can't understand how exactly to do it
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import Movie from './views/Movie.vue'
Vue.use(Router)
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
},
{
path: '/movie/:m_id',
name: 'movie',
component: Movie
}
]
})
import Navbar from '../components/Navbar'
import axios from "axios"
export default {
components:{
Navbar
},
data () {
return {
movi: null,
}
},
mounted () {
axios
.get(`https://api.themoviedb.org/3/movie/${m_id}?api_key=7bc75e1ed95b84e912176b719cdeeff9&language=en-US`)
.then(response => (this.movi= response.data))
}
}
I am trying to pass to axios this id of the page to get information about that specific movie and I got stuck.
Any help?
You can try this to use your params from the URL:
// Retrieve the `m_id` param from the `this.$route.params` object:
this.$route.params.m_id
For more info see https://router.vuejs.org/api/#route-object-properties
#How can I do the same thing but in Vuex
import Vue from 'vue'
import Vuex from 'vuex'
import Axios from 'axios';
import router from './router'
Vue.use(Vuex)
Vue.use(Axios)
Vue.use(router)
export default new Vuex.Store({
// data() {
// return {
// m_id:this.$route.params.m_id
// }
// },
// m_id : this.$route.params.m_id,
state: {
video_key: [],
},
mutations: {
updateInfo (state , video_key){
state.video_key = video_key
}
},
getters:{
m_id : this.route.params.m_id
},
actions: {
fetchData({commit,getters}){
axios.get(`https://api.themoviedb.org/3/movie/${this.m_id}/videos?api_key=7bc75e1ed95b84e912176b719cdeeff9&language=en-US`)
.then(response =>{
commit('updateInfo',response.data.results[0].key)
})
}
}
})

VueJS how to register global before guards

beforeEach hook for every route like in the docs:
My page should scroll to the top on route change.
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
window.scrollTo(0, 0)
next();
})
but my router has another structure and its not working:
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '#/components/HelloWorld'
import Contact from '#/components/Contact'
Vue.use(Router)
export default new Router({
beforeEach: (to, from, next) => {
window.scrollTo(0, 0)
next();
},
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/kontakt',
name: 'Contact',
component: Contact
},
]
})
Thanks in advance =)
Or is it better to use the .created hook on the components to scroll to the top of the page?
created() {
window.scrollTo(0, 0);
}
You can adapt your code to the structure required in the docs:
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '#/components/HelloWorld'
import Contact from '#/components/Contact'
Vue.use(Router)
// create object router with the valid initialization
const router = new Router({
routes: [{
path: '/',
name: 'HelloWorld',
component: HelloWorld
}, {
path: '/kontakt',
name: 'Contact',
component: Contact
}, ]
});
// add the beforeEach hook
router.beforeEach((to, from, next) => {
window.scrollTo(0, 0);
next();
});
// export router as default
export default router;
I think you should put Router.beforeEach after creating router.