Vue Router beforeRouteEnter API call response not 'working' - vue.js

I cannot see why the function is not returning its call back correctly.
I have a list of vehicle on one route and then another route to display that vehicles resource.
This is what I have in my Single file component for the single vehicle resource:
export default {
data() {
return {
vehicle: null,
error: null
}
},
created() {
},
beforeRouteEnter(to, from, next) {
function getVehicle(id) {
console.log('called');
return axios.get(`/api/v1/vehicles/${id}`);
}
getVehicle(to.params.id, (err, vehicle) => {
console.log('response');
next(vm => vm.setData(err, vehicle))
});
},
methods: {
setData (err, vehicle) {
if (err) {
this.error = err.toString();
} else {
this.vehicle = vehicle;
}
}
}
}
This issue is that when clicking a vehicle link, the API call is correctly made and returning a valid response but yet the next() method isn't called and the console log for 'response' isn't shown neither but the 'called' one is.

Not sure on the logic behind it, but had to use .then() for my getVehicle response before it would work. So changed to:
getVehicle(to.params.id).then( response => {
next( vm =>
vm.setData(response)
);
});

I'm not sure it will work with a function inside the beforeRouteEnter guard. Instead try call the axios get method like
let data = axios.get('/api/v1/vehicles/${to.params.id})
next(vm.setData(err, data))
This should work

Related

Calling methods inside beforeRouteEnter NProgress

I am trying to call an API before navigating to the route. The problem is that if I try to call axios call inside beforeRouteEnter it is working fine for example:
{
beforeRouteEnter(routeTo, routeFrom, next) {
NProgress.start()
axios.get('https://jsonplaceholder.typicode.com/posts').then((res) => {
next((vm) => {
vm.data = res.data
})
NProgress.done()
})
},
}
But when I try to call an API from methods it's navigating to the route before resolving an API and also NProgress bar is also completing before resolving a call.
{
beforeRouteEnter(routeTo, routeFrom, next) {
NProgress.start()
next((vm) => {
vm.index()
NProgress.done()
})
},
methods: {
index() {
axios
.get('https://jsonplaceholder.typicode.com/posts')
.then((res) => {
console.log(res.data)
})
.catch((error) => {
console.log(error)
})
},
},
}
Can anyone guide me what may be wrong?
In your first example, you set the progress bar, then you call the API with Axios and with .then you chain a function after the call. This means the function will wait until the promise is resolved or rejected, before continuing. Only when the axios call is finished successfully, the next function is executed in which you set the data and stop the progress bar. You also should use .catch for if the promises rejects.
Now in your second example, you do not use promises in beforeRouteEnter. Which basically means that all lines are executed immediately. So you call vm.index() and without waiting for the axios call to finish the next line, NProgress.done() will be executed. Although there are several ways to solve this my preference is use async/await, which is just a cleaner way to use promisses and chaining.
In your case I think this would work:
beforeRouteEnter(routeTo, routeFrom, next) {
NProgress.start();
await vm.index();
NProgress.done();
next();
});
}
And the method:
methods: {
async index () {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
console.log(response);
} catch (error) {
console.log(error);
}
}
}
Since this is only a part of your component I cannot test it, but I think you get the idea.

ASync/Await is not working as expected in router.BeforeEach guard in vue?

this is my router guard :
router.beforeEach(async (to,from,next)=>{
await store.dispatch('GetPermission');
if(to.matched.some(record => record.meta.requireAuth)){
let permissions=store.state.permissions; //getting empty
console.log(permissions);
if(permissions.filter(per => (per.name === 'read_list').length!=0)){
next({
path:'/dashboard/create'
})
}
else{
next()
}
}
// else if(to.matched.some(record => record.meta.requireAuth)){
// if(store.token!=null){
// next({
// path:'/dashboard'
// })
// }
// else{
// next()
// }
// }
else{
next()
}
});
problem is here though i m using await in dispatch method , i m not getting state value of permissions which is initially empty
here is vuex store code :
GetPermission(context){
axios.defaults.headers.common['Authorization']='Bearer ' + context.state.token
axios.get('http://127.0.0.1:8000/api/user').then((response)=>{
console.log(response)
context.commit('Permissions',response.data.permission)
})
//mutation:
Permissions(state,payload){
state.permissions=payload
}
//state
state:{
error:'',
token:localStorage.getItem('token') || null,
permissions:'',
success:'',
isLoggedin:'',
LoggedUser:{}
}
help me to solve it please ??
actions in Vuex are asynchronous. The only way to let the calling function (initiator of action) to know that an action is complete - is by returning a Promise and resolving it later.
Here is an example: myAction returns a Promise, makes a http call and resolves or rejects the Promise later - all asynchronously
actions: {
myAction(context, data) {
return new Promise((resolve, reject) => {
// Do something here... lets say, a http call using vue-resource
this.$http("/api/something").then(response => {
// http success, call the mutator and change something in state
resolve(response); // Let the calling function know that http is done. You may send some data back
}, error => {
// http failed, let the calling function know that action did not work out
reject(error);
})
})
}
}
Now, when your Vue component initiates myAction, it will get this Promise object and can know whether it succeeded or not. Here is some sample code for the Vue component:
export default {
mounted: function() {
// This component just got created. Lets fetch some data here using an action
this.$store.dispatch("myAction").then(response => {
console.log("Got some data, now lets show something in this component")
}, error => {
console.error("Got nothing from server. Prompt user to check internet connection and try again")
})
}
}
Also,you are calling same route when no permission match, in that case it always call your same route and make infinite loop.
Redirect to access denied page if permission denied.

vue router next() function don't work in Promise in router.beforeEach

I have the following code:
router.beforeEach((to, from, next) => {
if (to.name !== from.name) {
store
.dispatch("fetchCurrentUser")
.then(() => {
console.log('then');
// do something
next();
})
.catch(() => {
console.log('catch');
router.push("/login");
next();
});
} else {
next();
}
// next();
});
I'm trying to get the current user, and if this succeeds, then do something with this data, and if the request is not successful, then redirect the user to the login page. But next () calls do not work, I get the "then" or "catch" in the console, but the redirect does not occur and an infinite loop begins. But if I take next () from condition (commented row) the redirect works fine.
To redirect you should use next('/') or next({ path: '/' }).
From the documentation:
next: Function: this function must be called to resolve the hook. The
action depends on the arguments provided to next:
next(): move on to the next hook in the pipeline. If no hooks are
left, the navigation is confirmed.
next(false): abort the current navigation. If the browser URL was
changed (either manually by the user or via back button), it will be
reset to that of the from route.
next('/') or next({ path: '/' }): redirect to a different location.
The current navigation will be aborted and a new one will be started.
You can pass any location object to next, which allows you to specify
options like replace: true, name: 'home' and any option used in
router-link's to prop or router.push
The promise resolves after the function ends.
This means that the commented next happens regardless of the result of the promise result. Then the promise resolves and you call another next.
The bottom line is that you don't need the commented next and should just cover the promise resolve.
I was able to implement an async validation inside beforeEach, authentication in my case.
export async function onBeforeEach(to, from, next) {
let { someUserToken } = to.query
if (someUserToken) {
let nextRoute = await authenticate(to)
next(nextRoute)
} else {
const userToken = store.state.application.userToken
if (!to.meta.public && !userToken) {
next({ name: 'Forbidden' })
} else {
next()
}
}
}
async function authenticate(to) {
let { someUserToken, ...rest } = to.query
return store
.dispatch('application/authenticate', {
someUserToken
})
.then(() => {
return {
name: 'Home',
query: {
...rest
}
}
})
.catch(() => {
return { name: 'Forbidden' }
})
}
I hope this helps.

Execute custom functions one after another - Callback logic in Vue.js

There is a form which submits some data to an API in my component. Assume that it's method is ProcessLogin(). Inside this function I have written my API calls using axios. With the help of then() I have handled my server response and displayed my toast. All good.
Now as a part of my code clean up, I have decided to move all my axios functions to another api.js file and export functions from there. Here is an example function I have in my api.js file :
function ApiLogin(data) {
const url = `${BASE_URL}/authenticate`;
axios.post(url,data).then(response => {
return response;
}).catch(error => {
return error.response;
});
}
On the other side in my component I have my method defined as below :
methods: {
ProcessLogin() {
var status = ApiLogin(this.data);
console.log(status);
}
}
When executing this, I get undefined on my console. I know why it is happening. Because console.log(status) executes before ApiLogin could process and sends it's response. How to handle this kind of situation.? I know that callback is the rescue here, but I am not really sure about how to integrate it.
If you return the axios call from your ApiLogin function:
function ApiLogin(data) {
const url = `${BASE_URL}/authenticate`
return axios.post(url, data)
}
You could then handle the response in your component using then and console log from there:
methods: {
ProcessLogin() {
ApiLogin(this.data)
.then(res => console.log(res))
.catch(err => console.log(err))
}
}
...or with async/await:
methods: {
ProcessLogin: async function() {
try {
var status = await ApiLogin(this.data)
console.log(status)
}
catch(err) {
console.log(err)
}
}
}

VueRouter - Fetching Before Navigation - Multiple AJAX

I would like to use the beforeRouteEnter guard so I can be sure my data is loaded before going to a page. I read the example you can find here in the vue-router documentation.
Current situation
I'm more or executing two AJAX calls to get some data in the created lifecycle event.
export default {
created() {
const _this = this;
axios.get('/getCompanyDetails').then((response) => {
_this.private.company_details = response.data
});
axios.get('/getusers').then((response) => {
if(response.data){
_this.private.company_users = response.data;
}
});
}
}
What I try
beforeRouteEnter (to, from, next) {
function getCompanyDetails() {
return axios.get('/getCompanyDetails')
}
function getUsers() {
return axios.get('/getusers');
}
axios.all([getCompanyDetails(), getUsers()])
.then(axios.spread(function (company_details, company_users) {
next(vm => vm.setData(err, company_details, company_users))
}));
},
Am I on the right track ? The only thing I see here is I fell I'm required to call only one function setData in the next with all the parameters received from the different AJAX calls.
Is there a way to call several functions like setUsers(), setDetails() in the next ?
Is there a better way than what I'm doing ?
As #thanksd stated :
next((vm) => { vm.setUser(err, company_users); vm.setDetails(err, company_details); })
The final answer is then
beforeRouteEnter (to, from, next) {
function getCompanyDetails() {
return axios.get('/getCompanyDetails')
}
function getUsers() {
return axios.get('/getusers');
}
axios.all([getCompanyDetails(), getUsers()])
.then(axios.spread(function (company_details, company_users) {
next((vm) => { vm.setUser(err, company_users); vm.setDetails(err, company_details); })
}));
},