Vue-router param not updating with back button - vue.js

I am using a param, and when I push the param using this.$router.push() it works.
routes: {
path: ':stepId?',
name: 'stepper'
}
BUT, I am also watching $route inside a component in order to catch the value of the param changing (As described in the docs):
watch: {
$route: {
handler: function(to, from) {
const newStepId = (to.params && to.params.stepId) || this.steps[0].id;
const initial = !from;
if (initial || newStepId !== from.params.stepId) {
this.goToStep(newStepId, initial);
}
},
immediate: true
}
}
However when I use the back button, either the to section of the route inside the watch: $route doesn't have any param, just the path OR the watch doesn't even run.

I had the same issue, what worked for me was adding the $watch in the created() method.
created() {
this.$watch("$route",() => {
// this.$route.query is watched now as expected
},
{ immediate: true });
}
Still a bit unclear to me though why putting it in mounted or like what you did doesn't work

Related

Nuxt watch does not redirect

I have a Nuxt application with profile page. This page has a watcher which checks store.state.auth.isAuthenticated value. If it is false watcher should redirect to login page. The weird is that although the condition is evaluated right it does not redirect to login.
watch: {
'$store.state.auth.isAuthenticated': {
imediate: true,
deep: false,
handler(newVal) {
if( !newVal ) this.$router.push({'name': 'login'});
}
},
},
As I wrote above, condition is evaluated right but it does not trigger $router.push(). I dont understand it. What is wrong with that code?
EDIT: It creates the endless loop in auth.js middleware.
import { createNavigationGuard } from "~/plugins/navigation-guard.js";
export default function (context) {
if (process.client) {
const watchedStores = [];
const unwatchStore = (unwatch) => {
if (typeof unwatch === "function") {
unwatch();
}
};
// TODO: Find out whether the watchers persist after each route
// Unwatch previous route - this could be necessary for performance
console.log('watchedStores');
console.log(watchedStores);
unwatchStore(watchedStores.pop());
const unwatch = context.store.watch(
(state) => {
return state.auth.isAuthenticated;
},
(isAuthenticated) => {
createNavigationGuard(context, isAuthenticated);
},
{
immediate: true,
}
);
// it's not necessary to reassign unwatched variable to undefined with array
watchedStores.push(unwatch);
}
if (process.server) {
createNavigationGuard(
context,
context.store.state.auth.isAuthenticated
);
}
}
have you tried to make a method for the redirect and just call that method in your watch handler?
so instead of this.$router.push do this.redirectUser()
and in the method 'redirectUser()' do:
this.$router.push({'name': 'login'})

Redo the api call everytime I change the data value on VueJs

I'm trying to update a request of an axios.get
I have a method that adds 1 to the param data (the default value is 1), but even thought I'm updating the param value, the page won't change the content because it's not updating the get requisition
I know there something similar in react with componentDidUpdate method
Here's my code
Api request
async created() {
const {
data: {
data: { items, pagination },
},
} = await this.$axios.get(`/faq?page=${this.param}`)
},
Method:
methods: {
next() {
this.param = this.param + 1
},
},
So is it possible to redo the create() everytime i use the method next?
created() hook is called only once during a lifecycle, you can use watcher instead in order to listen to variable changes
watch: {
param: {
immediate: true,
handler(newVal, oldVal) {
if (newVal !== oldVal) {
await this.$axios.get(`/faq?page=${newVal}`)
}
}
}
}
For more info, please take a look at: https://v2.vuejs.org/v2/guide/computed.html#Computed-vs-Watched-Property

When passing data from parent component to child component via props, the data appears to be undefined in the mounted hook of the child component

In my parent component:
<UsersList :current-room="current_room" />
In the child component:
export default {
props: {
currentRoom: Object
},
data () {
return {
users: []
}
},
mounted () {
this.$nextTick( async () => {
console.log(this.currentRoom) // this, weirdly, has the data I expect, and id is set to 1
let url = `${process.env.VUE_APP_API_URL}/chat_room/${this.currentRoom.id}/users`
console.log(url) // the result: /api/chat_room/undefined/users
let response = await this.axios.get(url)
this.users = response.data
})
},
}
When I look at the page using vue-devtools, I can see the data appears:
I've run into this issue in the past – as have many others. For whatever reason, you can't rely on props being available in the component's mounted handler. I think it has to do with the point at which mounted() is called within Vue's lifecycle.
I solved my problem by watching the prop and moving my logic from mounted to the watch handler. In your case, you could watch the currentRoom property, and make your api call in the handler:
export default {
props: {
currentRoom: Object
},
data() {
return {
users: []
}
},
watch: {
currentRoom(room) {
this.$nextTick(async() => {
let url = `${process.env.VUE_APP_API_URL}/chat_room/${room.id}/users`
let response = await this.axios.get(url)
this.users = response.data
})
}
},
}
I don't think you really need to use $nextTick() here, but I left it as you had it. You could try taking that out to simplify the code.
By the way, the reason console.log(this.currentRoom); shows you the room ID is because when you pass an object to console.log(), it binds to that object until it is read. So even though the room ID is not available when console.log() is called, it becomes available before you see the result in the console.

Vue. How to route on current page

I have page '/users'.
export default {
name: 'Users',
created () {
const queryParams = this.$route.query
this[GET_USERS_FROM_SERVER](queryParams)
},
methods: {
...mapActions([GET_USERS_FROM_SERVER]),
onBtnFilterClick () {
this.$route.push('/users?minAge=20&name=alex&withPhoto=true')
}
}
}
When page started, it checks params and gets users from server. But it doesnt work and i think it is because router think that '/users' and '/users?params' is the same path.
If I add this.$router.go() after this.$router.go() it will reload current page and it works. But I want to do it in another way. How can I do this?
Don't reload the page if you do not have to.
this.$route.query can be just as reactive as your other data, so use this fact.
export default {
name: 'Users',
watch: {
'$route.query': {
immediate: true,
deep: true,
handler (queryParams) {
this[GET_USERS_FROM_SERVER](queryParams)
}
}
},
methods: {
...mapActions([GET_USERS_FROM_SERVER]),
onBtnFilterClick () {
this.$route.push('/users?minAge=20&name=alex&withPhoto=true')
}
}
}
When you watch for changes on $route.query, you call this[GET_USERS_FROM_SERVER] whenever it changes. I suspect that this changes the data in your component. I've set the immediate flag to run it when the component is created. I've set the deep flag, because this is an object, and I am not entirely sure if the query object gets replaced with every route change, or just modified. The deep flag will make sure that it will always trigger the handler.

vuejs2: how can i destroy a watcher?

How can i destroy this watcher? I need it only one time in my child component, when my async data has loaded from the parent component.
export default {
...
watch: {
data: function(){
this.sortBy();
},
},
...
}
gregor ;)
If you construct a watcher dynamically by calling vm.$watch function, it returns a function that may be called at a later point in time to disable (remove) that particular watcher.
Don't put the watcher statically in the component, as in your code, but do something like:
created() {
var unwatch = this.$watch(....)
// now the watcher is watching and you can disable it
// by calling unwatch() somewhere else;
// you can store the unwatch function to a variable in the data
// or whatever suits you best
}
More thorough explanation may be found from here: https://codingexplained.com/coding/front-end/vue-js/adding-removing-watchers-dynamically
Here is an example:
<script>
export default {
data() {
return {
employee: {
teams: []
},
employeeTeamsWatcher: null,
};
},
created() {
this.employeeTeamsWatcher = this.$watch('employee.teams', (newVal, oldVal) => {
this.setActiveTeamTabName();
});
},
methods: {
setActiveTeamTabName() {
if (this.employee.teams.length) {
// once you got your desired condition satisfied then unwatch by calling:
this.employeeTeamsWatcher();
}
},
},
};
</script>
If you are using vue2 using the composition-api plugin or vue3, you can use WatchStopHandle which is returned by watch e.g.:
const x = ref(0);
setInterval(() => {
x.value++;
}, 1000);
const unwatch = watch(
() => x.value,
() => {
console.log(x.value);
x.value++;
// stop watch:
if (x.value > 3) unwatch();
}
);
For this kind of stuff, you can investigate the type declaration of the API, which is very helpful, just hover the mouse on it, and it will show you a hint about what you can do: