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>
Related
I have a paginated component. The async setup() method is pulling data from an API to populate the page. It works fine when the route is directly loaded, but when I change the route to a different page slug (eg. clicking a router-link), the component is not reloaded and setup is not executed again to fetch the new data.
I guess I somehow want to force reloading the component?
This is my MainApp component it has the router view and fallback.
<router-view v-slot="{ Component }">
<Suspense>
<template #default>
<component :is="Component" />
</template>
<template #fallback>
loading...
</template>
</Suspense>
</router-view>
The router looks kinda like that. You see the page component takes a page_slug:
const routes: Array<RouteRecordRaw> = [
{
path: "/",
name: "",
component: MainApp,
children: [
{
name: "page",
path: "page/:page_slug",
component: Page,
props: true,
},
// [...]
]
}
And this is how my Page component looks like. It uses the page_slug to load data from an API which is then used in the template:
<template>
<div> {{ pageData }} </div>
</template>
export default defineComponent({
name: "Page",
props: {
page_slug: {
type: String,
required: true,
},
},
async setup(props) {
const pageData = await store.dispatch("getPageData", {
page_slug: props.page_slug
});
return { pageData }
}
}
When I directly open the route, the fallback "loading..." is nicely shown until the data is returned and the component is rendered.
But when I do a route change to another page, then async setup() is not executed again. In that case the url in the browser updates, but the data just remains the same.
How can I solve this case? Do I have to force reload the component somehow? Or have an entirely different architecture to the data loading?
The answer is simple, when trying to create Vue 3 Single File Components (SFCs) in Composition API way as shown below:
<template>
<!-- Your HTML code-->
</template>
<script>
export default {
name: 'ComponentName',
async setup():{
// Your code
}
};
</script>
<style>
/*Your Style Code*/
</style>
<script>, will only executes once when the component is first imported. So, when the data have changed by other component, the component above will not updated or in other words not re-created.
To make your component re-created whenever it about to mount, you have to use <script setup> which will make sure the code inside will execute every time an instance of the component is created, but you need to re-write your script code with few changes in comparison when using setup() method, and also you are able to use both of scripts like this:
<script>
// normal <script>, executed in module scope (only once)
runSideEffectOnce()
// declare additional options
export default {
name: "ComponentName",
inheritAttrs: false,
customOptions: {}
}
</script>
<script setup>
// executed in setup() scope (for each instance)
</script>
Read this documentation carefully to have full idea.
I'm trying to put a google sign in button inside my Vue2 project, so I tried to follow the instructions here https://developers.google.com/identity/gsi/web/guides/display-button#html
So I put this code below into my Hello.vue component
<template>
<section>
<div id="g_id_onload"
data-client_id="YOUR_GOOGLE_CLIENT_ID"
data-callback=myCallbackFunction
data-auto_prompt="false">
</div>
<div class="g_id_signin"
data-type="standard"
data-size="large"
data-theme="outline"
data-text="sign_in_with"
data-shape="rectangular"
data-logo_alignment="left">
</div>
</section>
</template>
<script>
export default {
methods: {
myCallbackFunction(){
}
}
}
</script>
and when I reloaded my page/component, it will display the error [GSI_LOGGER]: The value of 'callback' is not a function. Configuration ignored.
I think the problem is data-callback couldn't find or recognize myCallbackFunction which I already declared under methods. I've also tried to put myCallbackFunction under computed instead, but it still return the same error. So is there any way I can make this work?
Ok, I think I got it—but I switched from using the HTML documentation to the JavaScript documentation, since VueJS works better with this.
Still, I don't know if mounted is the best option, but it's at least working as intended.
Just use the callback function created at methods, and that's it.
mounted: function () {
google.accounts.id.initialize({
client_id:
'xxxxxxx.apps.googleusercontent.com',
callback: this.handleCredentialResponse,
})
google.accounts.id.prompt()}
working for me in Vue 2
<template>
<div>
<div id="signin_button"></div>
</div>
</template>
<script>
export default {
components: {
},
methods: {
handleCredentialResponse(response) {
console.log(response);
}
},
mounted: function () {
let googleScript = document.createElement('script');
googleScript.src = 'https://accounts.google.com/gsi/client';
document.head.appendChild(googleScript);
window.addEventListener('load', () => {
console.log(window.google);
window.google.accounts.id.initialize({
client_id: "xxxxxxx.apps.googleusercontent.com",
callback: this.handleCredentialResponse
});
window.google.accounts.id.renderButton(
document.getElementById("signin_button"),
{ theme: "outline", size: "large" } // customization attributes
);
})
}
}
</script>
use globalThis.yourcallbackfunction
I need help with the setup of my Vue application (I am just learning vue).
I read in the tutorials that to get access to a lifecycle hook I would need to do something like this:
<template>
<h4>Sports</h4>
<li v-for="sport in sports" v-bind:key="sport.id">
{{ sport.name }}
</li>
</template>
<script lang="ts">
import { onMounted } from "vue";
export default {
setup() {
onMounted(() => {
console.log("component mounted");
});
},
data() {
return {
sports: [],
};
},
};
</script>
However, VSCode's intellisense doesn't recognize onMounted as an exported function from vue. When I run my code in snowpack it still doesn't recognize the function.
I think the issue is likely due to lang="ts"
You could try having the js in a separate file and referencing it with <script lang="ts" src="./myComponent.ts"> and then have the typescript in there.
Here is some documentation someone came up with regarding, what seems to be, the same/related issue.
https://github.com/patarapolw/vue-typescript-suggestions
You don't need to import them, they are already available:
<template>
<h4>Sports</h4>
<li v-for="sport in sports" v-bind:key="sport.id">
{{ sport.name }}
</li>
</template>
<script>
export default {
data() {
return {
sports: [],
};
},
mounted() {
console.log("component mounted");
};
};
</script>
Also, unless you specifically want to use typescript, then leave off lang="ts" in the script tag.
I don't know I'm about quite Vue, spent a day learning all hooks routes, tried throug regular hooks created, mounted and it still gives me null. Here is my component App where all other components are plugged in. I'm calling here mounted hook, so
basically it totally rendered this component, and we have a path + I wrapped evrythin in $nextTick just to be sure and it still returns me null name of a current router if I come to the website not from main url, but from children as for instance url/boys. I gave up long time ago getting this name of a router in children component where I need it, thought alright I'll just path it to children <the-nav/> that's where I need it. But it doesnt work event in parent and I need only name of a current router when I enter the website. That's it, sounds easy but went throught hell.
<template>
<div id="app">
<the-header/>
<the-nav/>
<div id="app__inner">
<transition name="fade" mode="out-in">
<router-view />
</transition>
</div>
</div>
</template>
<script>
import TheHeader from "#/components/TheHeader.vue";
import TheNav from "#/components/TheNav.vue"
export default {
components : {
TheHeader,
TheNav
},
mounted()
{
this.$nextTick(() => {
console.log(this.$router.currentRoute.name);
})
}
}
</script>
You can actually use the exposed router functions inside this befoureRouteEnter function, used like so:
beforeRouteEnter (to, from, next) {
console.log(to.name);
next();
},
Please read some documentation on router Navigation guards:
https://router.vuejs.org/guide/advanced/navigation-guards.html
Make sure you have the name set on the router, otherwise name will be undefined. For example...
const routes = [
{
path: "/",
component: Home,
name: "Home" // Make sure route is named
},
];
Then in component you should be able to access the name in mounted hook, without nextTick (like mentioned in comment):
mounted() {
alert(`Route name is: ${this.$route.name}`);
}
DEMO
I developing a login/registration system in my Vue.js app. I want the items in navbar to be updated when I call this.$router.push('/').
App.vue:
<template>
<div id="app">
<Navbar></Navbar>
<router-view></router-view>
<Footer></Footer>
</div>
</template>
Navbar component:
export default {
name: "Navbar",
data: function() {
return {
isLoggedIn: false,
currentUser: null
}
},
methods: {
getAuthInfo: function() {
this.isLoggedIn = this.auth.isLoggedIn();
if (this.isLoggedIn) {
this.currentUser = this.auth.currentUser();
}
}
},
mounted: function() {
this.getAuthInfo();
},
updated: function() {
this.getAuthInfo();
}
}
Here is how I redirect to another page:
const self = this;
this.axios
.post('/login', formData)
.then(function(data) {
self.auth.saveToken(data.data.token);
self.$router.push('/');
})
.catch(function(error) {
console.log(error);
self.errorMessage = 'Error!';
});
SUMMARY: The problem is that isLoggedIn and currentUser in Navbar don't get updated when I call self.$router.push('/');. This means that functions mounted and updated don't get called. They are updated only after I manually refresh the page.
I solved the problem with adding :key="$route.fullPath" to Navbar component:
<template>
<div id="app">
<Navbar :key="$route.fullPath"></Navbar>
<router-view></router-view>
<Footer></Footer>
</div>
</template>
Check this out from the docs:
beforeRouteUpdate (to, from, next) {
// called when the route that renders this component has changed,
// but this component is reused in the new route.
// For example, for a route with dynamic params `/foo/:id`, when we
// navigate between `/foo/1` and `/foo/2`, the same `Foo` component instance
// will be reused, and this hook will be called when that happens.
// has access to `this` component instance.
},
I expect your Navbar component is reused across routes so its mounted and updated are not called. Try using beforeRouteUpdate if you want to do some processing on route change.