Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defer execution until external script has loaded in Vue.js

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?

like image 287
Mihai Avatar asked Feb 08 '18 19:02

Mihai


1 Answers

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()
    }
  }
}
like image 200
LShapz Avatar answered Sep 19 '22 12:09

LShapz