Unable to dispatch an action after updating the route using router.push - vue.js

When trying to dispatch an action after updating the route using this.$router, the action uses the previous route parameters instead of using the current route parameters.
Suppose I want to move to the /home and currently, I am on /about and in the store action called I want to access the route name, but after pushing the new route and then calling the action, it is using the previous route only and not the updated one.
this.$router.push('/home', this.$store.dispatch('called'))
Also, I tried using this.$router.push('/home').then(this.$store.dispatch('called')), but it gives undefined error.

You could pass the old and new queries from the method:
methods: {
changeRoute() {
const queryNew = { id: 1 }; // Create new query
const queryOld = { ...this.$route.query }; // Clone old query
this.$router.push({ path: '/home', query: queryNew }); // Navigate
this.$store.dispatch('called', { queryNew, queryOld }); // Call the action
}
}
The action signature should look like:
actions: {
called({ commit }, { queryNew, queryOld }) {
console.log(queryNew, queryOld);
}
}

Related

VueJS router.push not updating data inside page

I am using NuxtJS and I have a NavBar that goes to /website?web=1 and /website?web=2.. When I go from /website?web=1 to /website?web=2 vice versa.. My async fetch is not running at all.
website.vue
async fetch({ store, error, route, params }) {
let parameters;
const pageLimit = store.state.websites.pageLimit;
const offset = params.id ? (params.id - 1) * pageLimit : 0;
const web = route.query.web;
try {
if (web === "1") {
parameters = `?&is_global=true&offset=${offset}&limit=${pageLimit}`;
} else if (web === "2") {
parameters = `?&is_global=false&offset=${offset}&limit=${pageLimit}`;
} else {
parameters = `?co_id=${
route.query.co_id ? route.query.co_id : ""
}&ca_id=${
route.query.ca_id ? route.query.ca_id : ""
}&limit=${pageLimit}&offset=${offset}`;
}
await Promise.all([
store.dispatch("websites/fetchWebsites", parameters)
]);
} catch (e) {
console.log("Error: " + e);
}
},
NavBar.vue
methods: {
handleClick(tab, event) {
switch (tab.label) {
case "1":
this.$router.push({ name: "index" });
break;
case "2":
this.$router.push("/country");
break;
case "3":
this.$router.push("/website?web=1");
break;
case "4":
this.$router.push("/website?web=2");
break;
}
}
}
async fetch lifecycle is not invoked of query / param update
Sometimes you just want to fetch data and pre-render it on the server
without using a store. asyncData is called every time before loading
the page component. It will be called server-side once (on the first
request to the Nuxt app) and client-side when navigating to further
routes doc.
Also, a component does not remount on query / param update, so
lifecycles like created / mounted / beforeCreate etc are also not
invoked again. This helps in the application's performance as it avoids unnecessary rendering of the entire page where a few data changes would work.
Make a common method
methods: {
fetchData ({ store, error, route, params }) {
// your fetch logic here
let parameters;
const pageLimit = store.state.websites.pageLimit;
// ...
}
}
Call the method in async data
async fetch({ store, error, route, params }) {
this.fetchData({ store, error, route, params })
}
Call the method again on query change
watch: {
"$route.query.web": {
handler () {
this.fetchData({ store: this.$store, route: this.$route... });
}
},
Alternative to watch
beforeRouteUpdate (to, from, next) {
if (from.name === to.name) { // Call fn only when: route hasn't changed instead just query / params for the route has changed
this.fetchData({ store: this.$store, route: this.$route... })
}
},
When using Nuxt's fetch(), you need an explicit watcher to listen for route changes.
For a Nuxt component which has async fetch(), if you want it to update when the route changes, then setup a standard watcher.
See docs: https://nuxtjs.org/docs/2.x/features/data-fetching#listening-to-query-string-changes
export default {
watch: {
'$route.query': '$fetch' // This runs $fetch, defined below
},
async fetch() {
// Runs on server load (SSR), or when called (see above)
}
}
For other context's (or before Nuxt 2.12):
You could explore using watchQuery.
See docs: https://nuxtjs.org/docs/2.x/components-glossary/pages-watchquery/
export default {
watchQuery(newQuery, oldQuery) {
// Only execute component methods if the old query string contained `bar`
// and the new query string contains `foo`
return newQuery.foo && oldQuery.bar
}
}
https://nuxtjs.org/docs/2.x/components-glossary/pages-watchquery/

VueJS: $router.push not working with query parameters

In my NuxtJS(v. 2.10.2) application, I have a URL like below where pid is a post's id.
/post?pid=5e4844e34202d6075e593062
This URL works fine and loads the post as per the value passed to the pid query parameter. However, user can add new post by clicking Add Post button on the application bar that opens a dialog. Once the user clicks add, a request to back-end server is made to save the request. And once successful, user is redirected to the new post using vue router push like below
.then(data => {
if (data) {
this.$router.push({ path: `/post?pid=${data.id}` });
}
})
The problem is, user is not redirected to the new post, only the query parameter pid is updated. I suspect VueJS does not acknowledge this as a different URL and hence does nothing.
How to fix this?
Update: As an alternative tried the syntax below but getting the same behavior.
this.$router.push({ path: "post", query: { pid: data.id } });
Say you have a component post.vue which is mapped with /post URL.
Now if you redirect the user to /post?pid=13, the post.vue component won't mount again if it's already mounted ie. when you are already at /post or /post?pid=12.
[1] In this case, you can put a watch on the route to know if the route has been changed.
watch: {
'$route.path': {
handler (oldUrl, newUrl) {
let PID = this.$route.query.pid
// fetch data for this PID from the server.
// ...
}
}
}
OR
[2] If the component post.vue is mapped with some route say /post.
You can also use the lifecycle -> beforeRouteUpdate provided by vue-router
beforeRouteUpdate (to, from, next) {
let PID = to.query.pid
// fetch data for this PID from the server.
// ...
next()
}
By changing the approach component data can be updated as per the new query string value. Here is how it can be done.
Rather than trying to push to the same page again with different query string. The query string pid itself can be watched for change and on update new data can be fetched and the component data can be updated. In NuxtJS(v. 2.10.2) apps, this can be achieved with watchQuery. watchQuery is a NuxtJS property which watches changes to a query strings. And once it detects the change, all component methods(asyncData, fetch, validate..) are called. You can read more https://nuxtjs.org/api/pages-watchquery/
As for the solution, pushing to the same page with new query string remains the same.
.then(data => {
if (data) {
this.$router.push({ name: 'post', query: { pid: data.id } });
}
})
However, on the page.vue, where the data is fetched from the server. We need to add watchQuery property.
watchQuery: ["pid"],
async asyncData(context) {
let response = await context.$axios.$get(
`http://localhost:8080/find/id/${context.route.query.pid}`
);
return { postData: response };
},
data: () => ({
postData: null
})
Now, everytime the query string pid will change asyncData will be called. And that is it. An easy fix to updating component data when the query string value change.
try this solution
.then(data => {
if (data) {
this.$router.push({ name: 'post', query: { pid: data.id } });
}
})
hints:
// with query, resulting in /register?plan=private
router.push({ path: 'register', query: { plan: 'private' } })
Use watchQuery property (https://nuxtjs.org/docs/2.x/components-glossary/pages-watchquery)
export default {
watchQuery: true,
data: () => ...
}
In case anybody was looking for this:
Query parameters specified as a string do not work when passed to a path parameter:
router.push({path: 'route?query=params'})
When you want you use them as a string, just pass the whole string as an argument, like so: router.push('route?query=params')
It'll then be automagically picked by router and navigation will happen.
try this :
.then(data => {
if (data) {
this.$router.push('/post?pid=' + data.id);
}
})
hope it works!!!

Nuxt refresh router view when router :id parameter changes

My Nuxt app loads a link and it's child view on the route http://127.0.0.1/user/:id. I put the API calls for the user in mounted hook of the router view.
If route id changes, mounted is not triggered anymore because the child view is already loaded. I ended up with solution - watch the $route.params.id and moved the API call from mounted to this watcher.
watch: {
$route() {
this.getRows()
}
}
Is there a better way to do this?
Solution 1
Force the reload when the route changes defining a :key for the <nuxt-child>, like this:
<nuxt-child :key="$route.fullPath"></nuxt-child>
Solution 2
Put the API call to load the user in a watch to the id coming from the URL instead to mounted, you can use immediate: true to call it in the fist load.
export default {
data() {
return {
user: null
}
},
asyncData({ params }) {
return {
id: params.id
}
},
watch: {
id: {
immediate: true,
handler(id) {
//Call the API to get the user using the id
}
}
}
}

How do I update the Apollo data store/cache from a mutation query, the update option doesn't seem to trigger

I have a higher order component in my react native application that retrieves a Profile. When I call an "add follower" mutation, I want it to update the Profile to reflect the new follower in it's followers collection. How do I trigger the update to the store manually. I could refetch the entire profile object but would prefer to just do the insertion client-side without a network refetch. Currently, when I trigger the mutation, the Profile doesn't reflect the change in the screen.
It looks like I should be using the update option but it doesn't seem to work for me with my named mutations. http://dev.apollodata.com/react/api-mutations.html#graphql-mutation-options-update
const getUserQuery = gql`
query getUserQuery($userId:ID!) {
User(id:$userId) {
id
username
headline
photo
followers {
id
username
thumbnail
}
}
}
`;
...
const followUserMutation = gql`
mutation followUser($followingUserId: ID!, $followersUserId: ID!) {
addToUserFollowing(followingUserId: $followingUserId, followersUserId: $followersUserId) {
followersUser {
id
username
thumbnail
}
}
}`;
...
#graphql(getUserQuery)
#graphql(followUserMutation, { name: 'follow' })
#graphql(unfollowUserMutation, { name: 'unfollow' })
export default class MyProfileScreen extends Component Profile
...
this.props.follow({
variables,
update: (store, { data: { followersUser } }) => {
//this update never seems to get called
console.log('this never triggers here');
const newData = store.readQuery({ getUserQuery });
newData.followers.push(followersUser);
store.writeQuery({ getUserQuery, newData });
},
});
EDIT: Just realised that you need to add the update to the graphql definition of the mutation.
EDIT 2: #MonkeyBonkey found out that you have to add the variables in the read query function
#graphql(getUserQuery)
#graphql(followUserMutation, {
name: 'follow',
options: {
update: (store, { data: { followersUser } }) => {
console.log('this never triggers here');
const newData = store.readQuery({query:getUserQuery, variables});
newData.followers.push(followersUser);
store.writeQuery({ getUserQuery, newData });
}
},
});
#graphql(unfollowUserMutation, {
name: 'unfollow'
})
export default class MyProfileScreen extends Component Profile
...
this.props.follow({
variables: { .... },
);
I suggest you update the store using the updateQueries functionality link.
See for example this post
You could use compose to add the mutation to the component. Inside the mutation you do not need to call client.mutate. You just call the mutation on the follow user click.
It might also be possible to let apollo handle the update for you. If you change the mutation response a little bit that you add the following users to the followed user and add the dataIdFromObject functionality. link

Params field is empty in $router.push

Consider this:
this.$root.$router.push({
path: '/dashboard',
params: { errors: 'error' },
query: { test: 'test' }
})
I use this in my component to redirect to another URL, and some error has occured. The problem is that when I want to access params field in dashboard component, it's empty. The query field works well. I'm trying to access it by this.$route.params.errors.
You can use params only with named paths (i think).
Example:
//route (in your router file should have "name")
{ path: '/errors', name: 'EXAMPLE', component: ... }
//navigating
this.$router.push({
name: 'EXAMPLE',
params: { errors: '123' }
});
Now it will have correct value in this.$route.params.
If you don't want to use named routes you can try this:
ES6
this.$root.$router.push({
path: `/dashboard/${error}`,
query: { test }
})
ES5
this.$root.$router.push({
path: '/dashboard/' + error,
query: { test: 'test' }
})
I faced the similar issue where in one of my views (component). I was trying to navigate (programmatically) from /foo/bar to /foo/bar/123, but the route param was not available later in the component. My relevant navigation code looked like below:
methods: {
save_obj() {
let vm = this;
// Make AJAX call to save vm.my_obj, and on success do:
let v = `${vm.$route.path}/${vm.my_obj.id}`;
console.log("Loading view at path: "+v);
vm.$router.push({ path: v });
},
...
}
It would print the expected log (e.g., Loading view at path: /foo/bar/112), however, the loading of data in the created() hook would not receive the value of route param. My failing created() code looked like below:
created: function() {
console.log("Loading object details.");
let vm = this;
let cid = vm.$route.params.id; // <---- This was the problem
vm.$http.get('api/'+cid)
.then(function (res) {
if (res.data.status == "OK") {
vm.my_obj = res.data.body;
} else {
vm.setStatusMessage(res.data.body);
}
})
.catch(function (error) {
console.log(error);
vm.setStatusMessage("Error: "+error);
});
}
The solution was indicated in the third note here quoted below :
Note: If the destination is the same as the current route and only
params are changing (e.g. going from one profile to another /users/1
-> /users/2), you will have to use beforeRouteUpdate to react to changes (e.g. fetching the user information).
I had to do the following in my component:
Change the line let cid = vm.$route.params.id; in created() to let cid = vm.course.id
and, add the following to the component:
beforeRouteUpdate(to, from, next) {
if (to.params.id) {
this.my_obj.id = to.params.id;
}
// Some other code specific to my app
next();
}
I hope this helps someone stuck with the similar issue.
If you want to send a parameter with a query parameter you can use that syntax like that
this.$router.push({
path: this.localePath(`/bookings/${requestReservation?.attributes?.booking_id}`),
query: { requestReservation: requestReservation }
})
You can access it on the next page like that
this.$route.query.requestReservation
If you want send it fro nuxt-link than its syntax like that
<nuxt-link
:to="{ path: '/bookings/'+ requestReservation.attributes.booking_id,
query: { requestReservation } }">
Booking
</nuxt-link>
You can access it on the next page same like previous
this.$route.query.requestReservation