I know this was asked a couple of times, but I do not understand something about watching for a route change in nuxt.
It doesn't work for me.
My code is:
watch: {
$route(to, from) {
console.log('route change to', to)
console.log('route change from', from)
},
},
Minimal reproducable example:
https://codesandbox.io/s/dreamy-feather-90gbjm
Expected behavior
show console logs on route change.
result
nothing happens
You could solve this by adding the watcher on the layout that the index and about pages are rendered.
| pages
| index.vue
| about.vue
| layouts
| base.vue
In layouts/base.vue
<template>
<Nuxt />
</template>
<script>
export default {
....
watch: {
$route(to, from) {
console.log('route change to', to)
console.log('route change from', from)
},
},
....
}
</script>
In index.vue and about.vue
<template>
... Many things here
</template>
<script>
export default {
layout: 'base',
...
}
</script>
Related
I have a single page application with the structure below.
|- App.vue
|- + Views
| |- Page.vue
|- + Components
| |- Slider.vue
EDIT 1: Solution thanks to #gengar.value
I solved the issue by passing params from Page.vue with
methods: {
emitIndex: function (index) {
this.$router.push({
name: "visualization",
params: { imgCat: "visualization", imgIndex: index },
});
},
}
App.vue containing the router-view container that is routing the Page.vue
Slider.vue is a component of App.vue
I want to pass index of clicked image and whole images data from Page.vue to App.vue then to Slider.vue in order to achieve decoupling Slider from Page for reusability purposes.
How can I pass user selected index from Page.vue too App.vue
I have tried to use, params, props and emit but failed.
Sample Page.vue
<template>
<div v-for="(item, index) in 3" :key="index"></div>
</template>
<script>
export default ({
data() {
return {
urls: ['url1', 'url2', 'url3']
}
}
})
</script>
Thanks in advance
EDIT 1: Solution thanks to #gengar.value
Problem solved by pushing params to router via Page.vue and listening it from Slider.vue as follows:
Page.vue
methods: {
passIndex: function (index) {
this.$router.push({
name: "visualization",
params: { imgCat: "visualization", imgIndex: index },
});
},
}
Slider.vue
watch: {
"$route.params.imgCat": function (val) {
this.state = val;
},
"$route.params.imgIndex": function (newVal) {
if (newVal != -1) this.imgState = newVal;
this.$router.push({ params: { imgIndex: -1 } });
}
My solution is a little bit complicated, but quite native since I only used Props and Emit.
You want to pass value between brother components, so you could simply try below:
App.vue
<template>
<div>
<Page :data="data" #syncData="syncData" />
<Slider :data="data" />
</div>
<template>
<script>
import Page from './Views/Page.vue'
import Slider from './Components/Slider.vue'
export default ({
components: {
Page,
Silder
},
data() {
return {
data: [] // init data in the parent component
}
},
methods: {
syncData(updatedImages) {
this.data = updatedImages
}
}
})
</script>
Page.vue
<template>
<div></div>
<template>
<script>
export default ({
props: {
data: { type: Array, default: () => [] }
},
methods: {
onSelectImage(images) {
this.$emit('syncData', images) // update selected data to App.vue
}
}
})
</script>
Slider.vue
<template>
<div></div>
<template>
<script>
export default ({
props: {
data: { type: Array, default: () => [] }
},
watch: {
data: {
handler(val) {
// when Page.vue emits updated data to App.vue,
// App.vue will pass data to Slider.vue
// and you could receive the updated data 'val' here
},
deep: true
}
}
})
</script>
Update: Sorry I misunderstood before. If you are using vue-router components and assigned different paths (eg. '/page' and '/slider'), you can use
this.$router.push({ path: '/path', query: selectedImage })
in Page.vue and get url query in Slider.vue.
Alternative methods could be using Cookie.js or sessionStorage (not pretty tho). Also you could try Vuex if the specific condition suits you.
I am have been trying to get this to work, but it doesn't seem to work. I am trying to use the beforeRouteUpdate in my component in nuxt. What I want is for an api request to be made when I try to leave that page to another page. Here is my code;
<template>
<div>
//code here
</div>
</template>
<script>
export default {
beforeRouteUpdate(to, from, next) {
console.log('Before update')
},
}
</script>
In the code above, I am trying to log a message to the console when the route is updated, and nothing seems to happen. I tried making a request to the server when the route changes, that doesn't also work.
Tried using this other way;
<template>
<div>
//code here
</div>
</template>
<script>
export default {
watch: {
$route(to, from) {
console.log('Route path')
},
},
}
</script>
It still doesn't work. The current path is http://localhost:3000/cart. I am trying to console.log a message when the path changes to http://localhost:3000/ or something else.
I don't know if I am doing something wrong.
I am using console.log in this case for simplicity
Instead of beforeRouteUpdate use beforeRouteLeave:
<template>
<div>
//code here
</div>
</template>
<script>
export default {
beforeRouteLeave(to, from, next) {
console.log('Route Leave')
},
}
</script>
I have something that looks like this (in a Nuxt application):
<template>
<component :is="dynamicComponent"></component>
</template>
<script>
export default {
components: {
Component1,
Component2
},
created() {
this.$nuxt.$on("switch1", () => {
this.dynamicComponent = Component1;
});
this.$nuxt.$on("switch2", () => {
this.dynamicComponent = Component2;
});
},
data() {
return {
dynamicComponent: Component1
};
}
}
</script>
So an event triggers switching the component. However, if I try to switch from the default Component1 to Component2, Component2 briefly appears, only for the page to be reloaded with Component1. That seems to be such a basic thing and I have no idea why it's not working.
EDIT:
I found the mistake. Somehow during refactoring/reformatting the call got changed from:
<a class="nav-item" href="#" #click="$nuxt.$emit('switch2')">..</a>
to
<a class="nav-item" href="" #click="$nuxt.$emit('switch2')">..</a>
which triggers a reload of the page on click.
I want to have 3 main parts in my webapp:
App.vue - this page only has the <router-view> tag and some general configuration + it fetches an API every second
ControlPanel.vue - this page visualizes some data that the App.vue page gets
Profile.vue - this page visualizes some data that the App.vue page gets too
Right now I set up my App.vue with the API call and it passes the data it receives to the two pages with props like the following example. As you can see when it gets mounted it starts a loop that lasts 1 second where it goes and fetches the API and then it returns it to the two routes.
<template>
<div id="app">
<div id="nav">
<router-link :to="{ name: 'control_panel', params: { APIlogs } }">Control panel</router-link>
<span> | </span>
<router-link :to="{ name: 'profile', params: { APIlogs } }">Profile</router-link>
</div>
<router-view/>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
APIlogs: '',
};
},
mounted() {
setInterval(() => this.refreshData(), 1000);
},
methods: {
refreshData() {
axios.get('http://192.168.30.65:5000/logs')
.then((response) => {
this.APIlogs = response.data;
});
},
},
};
</script>
<style>
...
</style>
On the other hand, Control Panel and Profile are fundamentally the same page and they should get the props from the "father" and use it to visualize data but right now it doesn't work. When I click on one route it shows me the value the prop has in that moment and doesn't update as the App.vue page fetches more and more data.
<template>
<div id="app">
{{APIlogs}}
</div>
</template>
<script lang="ts">
import axios from 'axios';
export default {
name: 'control-panel',
props: ['APIlogs'],
data() {
return {
};
},
mounted(){
console.log(this.APIlogs);
},
methods: {
},
};
</script>
<style>
...
</style>
Did I do something wrong? Is my implementation good enough or is it lacking in some way? Really hope someone can help me out with this one, it's really tearing me apart.
Thanks a lot in advance
EDIT
Just to give a bit more context, before having props I was calling the same exact API from both components and it seemd very inefficient to me so I switched to this method.
Also my router.ts looks like this:
import Vue from 'vue';
import Router from 'vue-router';
import ControlPanel from '../src/components/ControlPanel.vue';
import Profile from '../src/components/Profile.vue';
Vue.use(Router);
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'control_panel',
component: ControlPanel,
props: true,
},
{
path: '/profile',
name: 'profile',
component: Profile,
props: true,
},
],
});
there's no params inside your paths i.e: path: '/:apilogs'
A dynamic segment is denoted by a colon :. When a route is matched,
the value of the dynamic segments will be exposed as
this.$route.params in every component.
(source)
After a while and almost an entire afternoon wasted on this problem, I found out this article which helped me achieve my goal. I just created a file with all my api calls and I call it every time I need to fetch something. It's a way more elegant and intelligent solution I think.
An easy way to make this work is to just make your APIlogs an object. Then it would be passed by reference and any updates to it will be reflected in the other components ..
export default {
data() {
return {
APIlogs: {logs: ''},
};
},
mounted() {
setInterval(() => this.refreshData(), 1000);
},
methods: {
refreshData() {
axios.get('http://192.168.30.65:5000/logs')
.then((response) => {
this.APIlogs.logs = response.data;
});
},
},
};
<template>
<div id="app">
{{APIlogs.logs}}
</div>
</template>
PS: You should probably use clearInterval in your beforeDestroy hook.
I have a couple routes in my vuejs SPA that I have set up using vue-router:
/create/feedback
/edit/feedback/66a0660662674061b84e8ea2fface0e4
The component for each route is the same form with a bit of smarts to change form values based on the absence or present of the ID in the route (feedbackID, in my example).
I notice that when I click from the edit route to the create route, the data in my form does not clear.
Below is the gist of my route file
import FeedbackFormView from './components/FeedbackForm.vue'
// Routes
const routes = [
{
path: '/create/feedback',
component: FeedbackFormView,
name: 'FeedbackCreate',
meta: {
description: 'Create Feedback',
}
},
{
path: '/edit/feedback/:feedbackId',
component: FeedbackFormView,
name: 'FeedbackEdit',
meta: {
description: 'Edit Feedback Form'
},
props: true
}
]
export default routes
Below is the gist of my component
<template lang="html">
<div>
<form>
<input v-model="model.someProperty">
</form>
</div>
</template>
<script>
export default {
data() => ({model: {someProperty:''}}),
props: ['feedbackId'],
created() => {
if (!this.$props['feedbackId']) {
return;
}
// otherwise do ajax call and populate model
// ... details omitted
}
}
</script>
However, if I modify my component as follows, everything works as expected
<template lang="html">
<div>
<form>
<input v-model="model.someProperty">
</form>
</div>
</template>
<script>
export default {
data() => ({model: {someProperty:''}}),
props: ['feedbackId'],
created() => {
if (!this.$props['feedbackId']) {
return;
}
// otherwise do ajax call and populate model
// ... details omitted
},
watch: {
'$route' (to, from) {
if (to.path === '/create/feedback') {
this.model = {}
}
}
}
}
</script>
Why is this? Why do I need watch?
I would have though that changing routes would be sufficient as the purpose of routing is to mimic the semantic behavior of page navigation
You have same component for different routes, when you go to edit route from the create route component is already created and mounted so the state of the component doesn't clear up.
Your component can listen to route changes using $router provided by vue-router every time the route changes the watcher is called.
For those who come this later, the following answer addresses the issue I was facing:
Vue-Router: view returning to login page after page refresh