I want to render a recaptcha
after a Vue.js
component has mounted. It works on normal load and reload, but when I navigate away to a different url
and click the browser back button it throws an error.
Here is my setup:
I am loading the api
script at the bottom of the page:
<script src='https://www.google.com/recaptcha/api.js' async></script>
on that page I render a globally registered component called Contact.vue
that contains a local component called Recaptcha.vue
:
<app-recaptcha @recaptchaResponse="updateRecaptchaResponse"></app-recaptcha>
The code for Recaptcha.vue
looks like this:
<template>
<div id="app-recaptcha">
<div :id="placeholderId"></div>
</div>
</template>
<script>
export default {
data() {
return {
sitekey: 'key_here',
placeholderId: 'grecaptcha',
widgetId: null
}
},
mounted() {
this.$nextTick(function () {
this.render();
});
},
methods: {
render() {
this.widgetId = window.grecaptcha.render(this.placeholderId, {
sitekey: this.sitekey,
callback: (response) => {
this.$emit('recaptchaResponse', response);
}
});
},
reset() {
window.grecaptcha.reset(this.widgetId);
}
}
}
</script>
<style>...</style>
On normal page load/ reload this.render()
is executed normally. However, when I navigate to another url
and return via the back button I get: Error in nextTick: "TypeError: window.grecaptcha.render is not a function"
.
I tried to:
set a variable on the onload
event of the api
script:
<script src='...' onload="window.script = { recaptcha: 'ready' }" async></script>
then add it as a property in data()
of Recaptcha.vue
:
ready: window.script.recaptcha
next, I added a watcher on the ready
property and within that watcher I tried to run this.render()
No success, the error is still there. I think that even in the normal load/ reload situation I am simply "lucky" that the api
script loads before the component gets mounted, and that placing this.render()
inside the mounted()
hook isn't helpful.
Do you know how can I signal Recaptcha.vue
that the external script has finished loading, and only then render the recaptcha
?
okay, here's a theory: add data properties
captchaReady: false,
checkingInterval: null,
created
let localThis = this
this.checkingInterval = setInterval(function(){
if (window.grecaptcha) {
localThis.captchaReady = true
}
}, 500) //or whatever interval you want to check
Then
watch: {
captchaReady: function(data) {
if (data) {
clearInterval(this.checkingInterval)
this.render()
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With