VueRouter sometime change URL but doesn't change component - vue-router

I'm new in vue3. One of my component have a strange reaction. I use vue router/ router-link tags in many components without problems, but in one of them, when I click a button, url correctly change but doesn't refresh to the new component until I press F5. The structure is not different from other components :(
Thanks in advance
Component :
<template>
<router-link :to="{ name: 'List', params : {pageId : 1 }}"><Btn msg="Retour à la liste des derniers messages"/></router-link>
<div class="originalMsg">
<h3>{{ originMsg.title }}</h3>
<p>Auteur : {{ originMsg.User.firstname +' '+ originMsg.User.lastname}} - Date de rédaction: {{ originMsg.creation_date }}</p>
<div class="content">
{{ originMsg.content }}<br>
<hr>
<router-link :to="{ name: 'NewMessage', query:{ responseTo : originMsg.id}}"><Btn msg="Répondre"/></router-link>
<router-link :to="{ name: 'Modify', query:{ id : originMsg.id, title : originMsg.title, content : originMsg.content}}"><Btn msg="Modifier"/></router-link>
</div>
</div>
<div v-if="responses.length>0" class="responses">
<p v-for="(response, index) in responses" :key="index"> {{ response }}</p>
</div>
</template>
<script>
import Btn from "../components/Button.vue";
export default {
name: "Message",
components:{
Btn
},
data(){
return{
originMsg:{},
responses:[],
}
},
async beforeCreate(){
let tmp = this.$route.query.parentMsg
if(this.$route.query.parentMsg==null){
tmp = this.$route.params.msgId
}
//récupère message d'origine
let parentMsg = await fetch(this.$store.state.src + 'message/' + tmp,{
method: "POST",
headers: {
'authorization': 'bearer ' + localStorage.getItem('token'),
'content-type': 'application/json'
},
body: JSON.stringify({userId:parseInt(localStorage.getItem('userId'))})
});
parentMsg = await parentMsg.json();
this.originMsg = parentMsg.msg;
//récupère tableau de réponses
let responseList = await fetch(this.$store.state.src + 'message/responses/' + tmp,{
method: "POST",
headers: {
'authorization': 'bearer ' + localStorage.getItem('token'),
'content-type': 'application/json'
},
body: JSON.stringify({userId:parseInt(localStorage.getItem('userId'))})
});
responseList = await responseList.json()
this.responses = responseList.list
}
};
</script>
router.js:
import { createRouter, createWebHistory } from 'vue-router'
import Connect from '../views/Connect.vue'
import Signup from '../views/Signup.vue'
import Reinit from '../views/Reinit.vue'
import Welcome from '../views/Welcome.vue'
import Profil from '../views/Profil.vue'
import List from '../views/List.vue'
import Message from '../views/Message.vue'
import NewMessage from '../views/NewMessage.vue'
import NotFound from '../views/NotFound.vue'
const routes = [
{
path: '/',
name: 'Login',
component: Connect
},
{
path: '/signup',
name: 'Signup',
component: Signup
}, {
path: '/reinit',
name: 'Reinit',
component: Reinit
}, {
path: '/home',
name: 'Welcome',
component: Welcome,
children: [{
path: 'list/:pageId',
name:"List",
component: List
},{
path: 'profil/:userId',
name:"Profil",
component: Profil
},{
path: 'message/:msgId',
name: "Message",
component: Message
},{
path: 'message/new',
name: "NewMessage",
component: NewMessage
},{
path: 'message/modify',
name : "Modify",
component: NewMessage
}]
},{ path: '/:pathMatch(.*)*', name: 'not-found', component: NotFound },
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
EDIT : I just see this but don't understand it
screenshot
EDIT2: for some old unknown reasons, I put, in an other component (List.vue), a :
watch: {
$route(to, from) {
if(to !== from){location.reload();}
}
}
When I remove it, it almost works. Now, routing is ok, but the destination component (ex: List) doesn't replace the Message component, but take place above. It's strange because I don't have router views in those components, only in their parent (Welcome.vue) component :(
List and Message components at the same time screenshot :(

Related

Navbar selects two drawers at once if one path contains the other in Vue

I'm creating an application in Vue.js with a sidebar.
In v-list-item, in the path :to="link.route", I'm using the names for routes: { name: "dashboard" }.
The problem occurs when the path to one link is contained in a link to the other path.
Eg. path: '/' (dashboard) is contained in path: '/help'.
If I click on Help, then both sidebar drawers are selected:
Template:
<template>
<div>
<v-navigation-drawer v-model="drawer">
<v-list nav dense>
<v-list-item-group>
<v-list-item
v-for="link in links"
:key="link.text"
:to="link.route">
<v-list-item-content>
<v-list-item-title class="white--text">{{
link.text
}}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list-item-group>
</v-list>
</v-navigation-drawer>
</div>
</template>
Script:
<script>
export default {
data: () => ({
drawer: true,
}),
computed: {
links() {
return [
{
text: "Dashboard",
route: { name: "dashboard" },
},
{
text: "Help",
route: { name: "help" },
},
];
},
},
};
</script>
Router:
import Vue from 'vue'
import VueRouter from 'vue-router'
import Dashboard from '../views/Dashboard.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'dashboard',
component: Dashboard
},
{
path: '/help',
name: 'help',
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
I fixed my problem by adding path { name: "dashboard", path:'/' }in dashboard route declaration.
Code before:
links() {
return [
{
text: this.$i18n.t("navbar.dashboard"),
route: { name: "dashboard"},
},
{
text: this.$t("navbar.help"),
route: { name: "help"},
},
];
},
Code after:
links() {
return [
{
text: this.$i18n.t("navbar.dashboard"),
route: { name: "dashboard", path:'/' },
},
{
text: this.$t("navbar.help"),
route: { name: "help"},
},
];
},
It's not exactly what I wanted, but it solves the problem.

Nuxt.js after page refresh meta are filled from config instead of head method

I had problem with meta in nuxt.js app. I want to fill dynamic meta tags in one detail page
--pages
----event
-----_id.vue
When I navigate on web site via link all work great. But if I just refresh page, meta tags use value from nuxt.config.js. For instance I got 'SiteTitle Nuxt.config.js' instead of 'SiteTitle - Some event title'.
Nuxt version 2.15.3
nuxt.config.js
export default {
head: {
titleTemplate: '%s - SiteTitle',
title: 'SiteTitle Nuxt.config.js',
htmlAttrs: {
lang: 'en'
},
meta: [
{charset: 'utf-8'},
{name: 'viewport', content: 'width=device-width, initial-scale=1'},
{hid: 'description', name: 'description', content: ''}
],
link: [
{rel: 'icon', type: 'image/x-icon', href: '/favicon.ico'}
]
}
components: true,
buildModules: [
'#nuxt/typescript-build',
'#nuxtjs/vuetify',
],
modules: [`enter code here`
'#nuxtjs/axios'
],
vuetify: {
customVariables: ['~/assets/variables.scss'],
},
axios: {
baseURL: 'https://api.someurl.com',
}
}
And _id.vue file
<template>
<v-card class="mt-6 mb-5" outlined>
<v-card-title>{{ model.title }}</v-card-title>
</v-card>
</template>
<script lang="ts">
import {Component, Vue} from "nuxt-property-decorator"
import {EventModel} from "~/api/models/EventModel";
import EventApi from "~/api/EventApi";
#Component({
async asyncData({params, $axios}) {
const eventApi = new EventApi($axios)
const model = await eventApi.get(parseInt(params.id))
return { model: model };
},
head(this: EventPage): object {
return {
title: "SiteTitle - " + this.model.title,
meta: [
{
hid: 'description',
name: 'description',
content: this.model.shortDescription
}
]
}
},
})
export default class EventPage extends Vue {
model = {} as EventModel
async fetch() {
}
}
</script>
I tried to call api in fetch, in this case meta values always have valid value when I refresh page, but Facebook Sharing Debugger get meta from nuxt.config.js in this case, and this solution is not suitable
Thanks for your help
You can do one thing
Create one plugin in that you can use this on router.beforeEach like this:
app.router.beforeEach(async(to, _, next) => {
document.title = to.meta.pageTitle ? ('Specify title - ' + ${to.meta.pageTitle}) :'Default Title';
})
In your router file you can do something like this:
{
path: '/path',
name: 'Name',
component: Component,
meta: {
pageTitle: 'Change Password'
}
}

Component doesn't load while the route changes

I want to create a nested route with its children's route.
Basically what I want is,
https://localhost/contact render the ContactList component.
https://localhost/contact/add render the ContactAdd component.
What I have tried is:
let Layout = {
template: '<div>Layout Page <router-view></router-view></div>'
};
let Home = {
template: '<div>this is the home page. Go to <router-link to="/contact">contact</router-link> </div>'
};
let ContactList = {
template: '<div>this is contact list, click <router-link to="/contact/add">here</router-link> here to add contact</div>'
};
let ContactAdd = {
template: '<div>Contact Add</div>'
}
let router = new VueRouter({
routes: [{
path: '/',
redirect: 'home',
component: Layout,
children: [{
path: 'home',
component: Home
},
{
path: 'contact',
component: ContactList,
children: [{
path: 'add',
component: ContactAdd
}]
},
]
}]
});
new Vue({
el: '#app',
components: {
'App': {
template: '<div><router-view></router-view></div>'
},
},
router
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vue-router#3.0.1/dist/vue-router.js"></script>
<section id="app">
<app></app>
</section>
Here, the problem is when I change the route from /client to /client/add, the view doesn't render. What is the problem with the nested children's route here? How to solve this issue?
I check this documentation https://router.vuejs.org/guide/essentials/nested-routes.html, but It didn't help in this case.
You need to add one <router-view> in the template of <ContactList> to load its children route.
Or if you'd like to display ContactAdd in Layout, moves ContactAdd to direct child of path=/.
let Layout = {
template: '<div>Layout Page <router-view></router-view></div>'
};
let Home = {
template: '<div>this is the home page. Go to <router-link to="/contact">contact</router-link> </div>'
};
let ContactList = {
// add <router-view> in order to load children route of path='/contact'
template: '<div>this is contact list, click <router-link to="/contact/add">Add Contact In sub Router-View</router-link> here to add contact<p><router-view></router-view></p> Or Click <router-link to="/addcontact">Add Contact In Current Router-View</router-link></div>'
};
let ContactAdd = {
template: '<div>Contact Add</div>'
}
let router = new VueRouter({
routes: [{
path: '/',
redirect: 'home',
component: Layout,
children: [{
path: 'home',
component: Home
},
{
path: 'contact',
component: ContactList,
children: [{
path: 'add',
component: ContactAdd
}]
},
{
path: 'addcontact', // or move ContactAdd as direct child route of path=`/`
component: ContactAdd,
}
]
}]
});
new Vue({
el: '#app',
components: {
'App': {
template: '<div><router-view></router-view></div>'
},
},
router
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vue-router#3.0.1/dist/vue-router.js"></script>
<section id="app">
<app></app>
</section>

VueJS props are not passing to component via router-link

I have the following link in a VueJS component:
<router-link :to="{ name: 'time', props: { tax: 123, site: site_key }}">Add New Request</router-link>
The Time component has the following code, its always showing the default, instead of the value:
<template>
<div>t {{ tax }} {{site}}</div>
</template>
<script>
export default {
name: "Time",
props: {
tax: {
type: String,
default: 'Vue!'
},
site: {
type: String,
default: 'Vue!'
}
}
}
</script>
EDITED
const router = new Router({
routes: [
{
path: '/',
name:'home',
component: Home,
},
{
path: '/time',
name:'time',
component: Time,
props: { tax: null, site: null }
},
]
})
As seen in Vue Router :
https://router.vuejs.org/guide/essentials/passing-props.html
Props are passed by the params field, so like this :
<router-link :to="{ name: 'time', params: { tax: 123, site: site_key }}">Add New Request</router-link>
for a much more insight on your problem, please include your router.js file.

route.params has properties that don't match dynamic url

I want to ask about vue-router. This is my first time using vue js.
Initially I was on the route:
localhost: 8080 / article / edit-article / 2
and when I move to route:
localhost: 8080 / page
this is still running.
But when I moved from:
localhost: 8080 / article / edit-article / 2
to:
locahost: 8080 / dashboard
the route instead changed to:
localhost: 8080 / article / edit-article / dashboard
not:
localhost: 8080 / dashboard
This my vue-router:
export default new Router({
mode: 'history',
linkActiveClass: 'open active',
scrollBehavior: () => ({ y: 0 }),
routes: [
{
path: '/',
redirect: '/login',
name: 'Home',
component: DefaultContainer,
children: [
{
path: 'dashboard',
name: 'Dashboard',
component: Dashboard,
meta: {
requiresAuth: true
}
},
{
path: 'page',
name: 'Page',
component: Page,
meta: {
requiresAuth: true
}
},
]
},
{
path: '/article',
component: DefaultContainer,
meta: {
requiresAuth: true
},
children: [
{
path: '',
name: 'List Articles',
component: Article,
meta: {
requiresAuth: true
}
},
{
path: 'add-article',
name: 'Add Article',
component: AddArticle,
meta: {
requiresAuth: true
}
},
{
path: 'edit-article/:id',
name: 'Edit Article',
component: EditArticle,
meta: {
requiresAuth: true
}
},
]
},
{
path: '/login',
name: 'Login',
component: Login,
meta: {
requiresVisitor: true
}
},
]
})
I use navbar with js, like this:
export default {
items: [
{
name: 'Dashboard',
url: 'dashboard',
icon: 'icon-speedometer',
},
{
name: 'Article',
url: 'article',
icon: 'icon-note'
},
{
name: 'Page',
url: '/page',
icon: 'icon-layers'
},
]
}
and this my DefaultContainer:
<template>
<div class="app">
<AppHeader fixed>
<SidebarToggler class="d-lg-none" display="md" mobile />
<b-link class="navbar-brand" to="/dashboard">
<img class="navbar-brand-full" src="img/brand/logo.svg" width="89" height="25">
<img class="navbar-brand-minimized" src="img/brand/sygnet.svg" width="30" height="30>
</b-link>
<SidebarToggler class="d-md-down-none" display="lg" />
<b-navbar-nav class="ml-auto">
<DefaultHeaderDropdownAccnt/>
</b-navbar-nav>
</AppHeader>
<div class="app-body">
<AppSidebar fixed>
<SidebarHeader/>
<SidebarForm/>
<SidebarNav :navItems="nav"></SidebarNav>
<SidebarFooter/>
<SidebarMinimizer/>
</AppSidebar>
<main class="main">
<Breadcrumb :list="list"/>
<div class="container-fluid">
<router-view></router-view>
</div>
</main>
<AppAside fixed>
<!--aside-->
<DefaultAside/>
</AppAside>
</div>
<TheFooter>
<!--footer-->
<div>
<span class="ml-1">© 2019 local Incorporation. All Rights Reserved</span>
</div>
</TheFooter>
</div>
</template>
<script>
import nav from '#/_nav'
import { Header as AppHeader, SidebarToggler,` `Sidebar as AppSidebar, SidebarFooter, SidebarForm, SidebarHeader, SidebarMinimizer, SidebarNav, Aside as AppAside, AsideToggler, Footer as TheFooter, Breadcrumb } from '#coreui/vue'
import DefaultAside from './DefaultAside'
import DefaultHeaderDropdown from './DefaultHeaderDropdown'`
export default {
name: 'DefaultContainer',
components: {
AsideToggler,
AppHeader,
AppSidebar,
AppAside,
TheFooter,
Breadcrumb,
DefaultAside,
DefaultHeaderDropdown,
SidebarForm,
SidebarFooter,
SidebarToggler,
SidebarHeader,
SidebarNav,
SidebarMinimizer
},
data () {
return {
nav: nav.items
}
},
computed: {
name () {
return this.$route.name
},
list () {
return this.$route.matched.filter((route) => route.name || route.meta.label )
}
}
}
</script>
Thanks all
It seems likely that the problem is here:
url: 'dashboard',
This would appear to be a relative path. So if you are currently on the page http://localhost:8080/article/edit-article/2 it will resolve relative to that URL, giving http://localhost:8080/article/edit-article/dashboard. This isn't really anything to do with Vue router, it's just how relative URLs are resolved. You'd get exactly the same behaviour if you used an HTML link such as <a href="dashboard">.
If you start your relative URL with a / then it will omit the rest of the path:
url: '/dashboard',
This should fix your problem. This is what you've done with your /page URL, which is presumably why that works.
Personally I would use named routes instead, rather than trying to craft relative URLs. See https://router.vuejs.org/guide/essentials/named-routes.html