DanSnow / vue-recaptcha

Google ReCAPTCHA component for Vue.js
http://dansnow.github.io/vue-recaptcha/
MIT License
869 stars 134 forks source link

How to use the library in Nuxt 2.14? #1668

Closed serious-angel closed 5 months ago

serious-angel commented 5 months ago

Description

Considering the documentation mainly oriented on Nuxt 3 and Vue 3, is it possible to anyhow use it in Nuxt 2.14 with Vue 2.6?

I've rewritten it to be more "Promise"-alike, and it's been in use vue-recaptcha@1.3.0 since mammoths probably. Yet, apparently, indeed this version does not support "action" in its "execute" method.

It's indeed a legacy project and I hope it will be upgraded at some point...

Minimal Reproducible Example

Currently, I've been using it like this:

<template>
  <vue-recaptcha
    ref="recaptcha"
    :load-recaptcha-script="true"
    :sitekey="siteKey"
    size="invisible"
    @render="onCaptchaRender"
  />
</template>

<script lang="ts">
import Vue from 'vue'

// @todo Replace with a modern and more adequate alternative that supports `Promise`.
import VueRecaptcha from 'vue-recaptcha'

export default Vue.extend({
  name: 'V3VueRecaptcha',

  components: {
    // In order to load it at Nuxt client-side only
    VueRecaptcha: () => import('vue-recaptcha')
  },

  props: {
    siteKey: {
      type: String,
      required: true
    }
  },

  data() {
    return {
      processing: false,
      initialized: false,
      ready: false,
      token: ''
    }
  },

  methods: {
    async execute(): Promise<string> {
      if (this.processing) {
        throw new Error('Already processing')
      }

      this.processing = true

      return await new Promise<string>((resolve, reject) => {
        // How to pass "action" for Recaptcha request?
        this.prepare(resolve, reject).execute()
      })
        .catch((error) => {
          this.onCaptchaError(error)

          throw error
        })
        .finally(() => {
          this.processing = false
        })
    },

    prepare(onVerify: (token: string) => void, onError: (error: Error) => void) {
      this.ready = false
      this.resetHandlers()

      const recaptcha = this.$refs.recaptcha as InstanceType<typeof VueRecaptcha>

      recaptcha.$once('error', (error: Error) => {
        this.onCaptchaError(error)
        onError(error)
      })

      recaptcha.$once('expired', () => {
        this.onCaptchaExpired()
      })

      recaptcha.$once('verify', (token: string) => {
        this.onCaptchaVerify(token)
        onVerify(token)
      })

      recaptcha.reset()
      this.ready = true

      return recaptcha
    },

    resetHandlers() {
      const recaptcha = this.$refs.recaptcha as InstanceType<typeof VueRecaptcha>
      recaptcha.$off('error')
      recaptcha.$off('expired')
      recaptcha.$off('verify')
    },

    onCaptchaRender(widgetId: number) {
      this.initialized = true
      this.$emit('render', widgetId)
    },

    onCaptchaError(error: Error) {
      console.error('[Captcha Error]', error)
      this.token = ''
      this.resetHandlers()
      this.processing = false
      this.$emit('error', error)

      if (this.$t) {
        throw new Error(this.$t('application.errors.captcha_request_failed') as string)
      }

      throw error
    },

    onCaptchaExpired() {
      this.token = ''
      this.resetHandlers()
      this.processing = false
      this.$emit('expired')
    },

    onCaptchaVerify(token: string) {
      this.token = token
      this.$emit('verify', token)
    }
  }
})
</script>

System info

$ yarn list --pattern 'nuxt'
yarn list v1.22.19
├─ @aceforth/nuxt-optimized-images@1.3.0
├─ @nuxt/babel-preset-app@2.14.6
├─ @nuxt/builder@2.14.6
├─ @nuxt/cli@2.14.6
├─ @nuxt/components@1.1.0
├─ @nuxt/config@2.14.6
├─ @nuxt/core@2.14.6
├─ @nuxt/devalue@1.2.4
├─ @nuxt/friendly-errors-webpack-plugin@2.5.0
├─ @nuxt/generator@2.14.6
├─ @nuxt/loading-screen@2.0.2
├─ @nuxt/opencollective@0.3.0
├─ @nuxt/server@2.14.6
├─ @nuxt/static@1.0.0
├─ @nuxt/telemetry@1.2.3
├─ @nuxt/types@2.14.6
├─ @nuxt/typescript-build@1.0.3
│  └─ @nuxt/types@0.7.9
├─ @nuxt/typescript-runtime@1.0.0
├─ @nuxt/utils@2.14.6
├─ @nuxt/vue-app@2.14.6
├─ @nuxt/vue-renderer@2.14.6
├─ @nuxt/webpack@2.14.6
├─ @nuxtjs/axios@5.12.2
├─ @nuxtjs/eslint-config-typescript@2.1.0
├─ @nuxtjs/eslint-config@3.1.0
├─ @nuxtjs/eslint-module@2.0.0
├─ @nuxtjs/proxy@2.0.1
├─ @nuxtjs/stylelint-module@4.0.0
├─ @nuxtjs/vuetify@1.12.3
├─ @nuxtjs/youch@4.2.3
├─ eslint-plugin-nuxt@1.0.0
├─ nuxt-i18n@6.15.1
└─ nuxt@2.14.6
$ yarn list --pattern 'vue'
yarn list v1.22.19
├─ @intlify/vue-i18n-extensions@1.0.2
├─ @intlify/vue-i18n-loader@1.0.0
├─ @nuxt/vue-app@2.14.6
├─ @nuxt/vue-renderer@2.14.6
├─ @nuxtjs/vuetify@1.12.3
├─ @vue/babel-helper-vue-jsx-merge-props@1.0.0
├─ @vue/babel-plugin-transform-vue-jsx@1.1.2
├─ @vue/babel-preset-jsx@1.1.2
├─ @vue/babel-sugar-functional-vue@1.1.2
├─ @vue/babel-sugar-inject-h@1.1.2
├─ @vue/babel-sugar-v-model@1.1.2
├─ @vue/babel-sugar-v-on@1.1.2
├─ @vue/component-compiler-utils@3.2.0
├─ @vue/reactivity@3.4.27
├─ @vue/runtime-core@3.4.27
├─ @vue/runtime-dom@3.4.27
├─ @vue/shared@3.4.27
├─ eslint-plugin-vue@6.2.2
├─ overlayscrollbars-vue@0.2.2
├─ vue-client-only@2.0.0
├─ vue-eslint-parser@7.1.0
├─ vue-hot-reload-api@2.3.4
├─ vue-i18n@8.21.1
├─ vue-lazy-hydration@1.0.0-beta.14
├─ vue-loader@15.9.3
├─ vue-meta@2.4.0
├─ vue-no-ssr@1.1.1
├─ vue-recaptcha@1.3.0
├─ vue-router@3.4.3
├─ vue-server-renderer@2.6.12
├─ vue-shortkey@3.1.7
├─ vue-style-loader@4.1.2
├─ vue-template-compiler@2.6.12
├─ vue-template-es2015-compiler@1.9.1
├─ vue-typed-mixins@0.2.0
├─ vue-youtube-embed@2.2.2
├─ vue@2.6.12
├─ vuetify-loader@1.9.2
├─ vuetify@2.7.2
├─ vuex-module-decorators@0.17.0
└─ vuex@3.5.1
serious-angel commented 5 months ago

Moved to GitHub repository Discussions: https://github.com/DanSnow/vue-recaptcha/discussions/1669