I'm trying to resolve a promise in the scrollBehavior function in vue 3. The vue router documentation is vague in regards to async scrolling (https://v3.router.vuejs.org/guide/advanced/scroll-behavior.html#async-scrolling).
With vue 2 I would have done something like this:
scrollBehavior: (to, from, savedPosition) => {
return new Promise((resolve) => {
const position = (function () {
if (savedPosition) {
return savedPosition;
}
})();
router.app.$root.$once("triggerScroll", () => {
router.app.$nextTick(() => resolve(position));
});
});
};
But since vue 3 removed the events api, this can no longer be done. How can I resolve the promise at the end of a page transition? I could use a library like tiny-emitter, but I would prefer to learn how to do this without.
In the router.js :
scrollBehavior: (to, from, savedPosition) => {
return new Promise((resolve) => {
const position = (function () {
if (savedPosition) {
return savedPosition;
}
})();
// Listen to an event here to resolve, but how ?
// resolve(position));
});
};
Transition afterEnter function:
function afterEnter(router, trans) {
// Do something here to trigger to resolve the promise
}
Thank you
Related
Im adding the scrollBehavior in vueRouter and I can't find anywhere how can I add the behavior: smooth on the savedPosition in a timeout?
here is my code where I would like to add the smooth option :
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(savedPosition);
}, 1000);
});
}
},
Thank you alot in advance.
Try this
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return new Promise((resolve) => {
setTimeout(() => {
savedPosition.behavior = "smooth"
resolve(savedPosition);
}, 1000);
});
}
},
I have the needings to use firebase auth with vue router.
I have this simple guard, but I've noticed that sometimes the users will see for a while the pages also if they are not logged.
router.beforeEach( async (to, from) => {
onAuthStateChanged( getAuth(app), (user) => {
console.log(user, to.meta.requireAuth)
if( to.meta.requireAuth && !user ) {
return {
name: 'Signin'
}
}
})
})
I also have this kind of control inside my components, but I'm looking for something global to use to prevent unregistered users to see the app.
Any suggestion?
You can wrap the onAuthStateChanged in a Promise and make your before each an async function.
// in some global file
export async function getCurrentUser(): Promise<User | null> {
return new Promise((resolve, reject) => {
const unsubscribe = auth.onAuthStateChanged((user) => {
unsubscribe();
resolve(user);
}, reject);
});
}
// your router file
router.beforeEach(async (to, from, next) => {
if (to.matched.some((record) => record.meta.publicAccess)) {
next();
} else {
const currentUser = await getCurrentUser();
if (currentUser) {
next();
} else {
next({ name: "Login" });
}
}
});
// Your route object
{
name: "Login",
path: "/login",
component: () => import("#/views/authentication/Login.vue"),
}
Currently I have defined in a functional component a useEffect as below
useEffect(() => {
(async function () {
posts.current = await BlogConsumer.getBlogPosts();
setLoading(false);
})();
return () => {
BlogConsumer.call_controller.abort();
};
}, []);
where this BlogConsumer is defined as below
class BlogConsumer {
static posts = {};
static call_controller = new AbortController();
static async getBlogPosts() {
await axios
.get('https://nice.api', {
signal: this.call_controller.signal,
})
.then(response => {
// treatment for success
})
.catch(error => {
// treatment for erros
});
return this.posts;
}
}
export default BlogConsumer;
The overral ideia is that in the render of the component I'll be calling a static method from my consumer and will retrieve the necessary data. For the pourpuse of not having memory leaks, I have my callback function in my useEffect that will abort my call whenever I unmount the component, but this is not working. React's message of Warning: Can't perform a React state update on an unmounted component. still appears if I enter the component and leave the screen before the API call is finished. I don't know where I am wrong, so I'd like a little help.
Thanks in advance.
You could just cancel the request on unmount. Like this:
export const fetchData = async (signal) => {
try {
const res = await instance.get("/pages/home", {
signal,
});
return res.data;
} catch (error) {
return Promise.reject(error);
}
};
useEffect(() => {
const controller = new AbortController();
fetchData(controller.signal);
return () => {
controller.abort()
};
}, []);
I'm trying to wait for an asynchronous response in my route guard. The problem is that two routes are hit.
When the page is loaded, the / route is triggered instantly, followed by my target route
router.beforeEach(async (to, from, next) => {
await new Promise(resolve => {
setTimeout(resolve, 500)
})
next()
})
or
router.beforeEach((to, from, next) => {
setTimeout(next, 500)
})
})
In my log I see two entries when visiting /foo, first / then /foo after 500ms. I'm expecting to see only the latter:
setup(props, { root }) {
const hideNav = computed(() => {
console.log(root.$route.path)
return root.$route.meta?.hideNav
})
return {
hideNav
}
}
})
I'm using vue#2.6.12 and vue-router#3.4.9
I'm not sure but this how I guard my route in a Vue component hope this helps you
beforeRouteEnter(to, from, next) {
if (store.getters.isAuthenticated) {
Promise.all([
store.dispatch(FETCH_ARTICLE, to.params.slug),
store.dispatch(FETCH_COMMENTS, to.params.slug)
]).then(() => {
next();
});
} else {
Promise.all([store.dispatch(FETCH_ARTICLE, to.params.slug)]).then(() => {
next();
});
}
},
Have you tried using beforeEnter?
This way you can specify which routes are going to execute your function
Like this code below:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
I have used this link as a reference to make a request before entering a route:
https://router.vuejs.org/en/advanced/data-fetching.html
import Vue from 'vue'
import VueResource from 'vue-resource'
Vue.use(VueResource)
function getCities () {
return Vue.http({
method: 'GET',
url: process.env.base_url + 'cities'
})
}
export default {
data () {
return {
cities: []
}
},
beforeRouteEnter (to, from, next) {
getCities((err, cities) => {
if (err) {
next(false)
} else {
next(vm => {
vm.cities = cities.data
})
}
})
},
watch: {
$route () {
this.cities = []
getCities((err, cities) => {
if (err) {
this.error = err.toString()
} else {
this.cities = cities.data
}
})
}
}
However it doesn't seem to be working for me. I have tested this code and the request is successfully being made. However the result is not being returned. Currently, the request itself is being returned from the function, but I cannot show it in the beforeRouteEnter callback where it supposedly should assign it to vm.cities neither in the watch $route section.
Any help/opinion is appreciated.
The Vue.http method returns a promise, so the code should read:
beforeRouteEnter (to, from, next) {
getCities().then(response => {
next(vm => vm.cities = response.body)
}
}