I have a simple vue form where I utilize a dependency called vue-turnstile which in turn adds the captcha the captcha from cloudflare to my form. My issue is I get a strange error using it and I cannot for the reason figure out why this wouldnt work. My warning is as follows:
[Vue warn]: Missing ref owner context. ref cannot be used on hoisted vnodes. A vnode with ref must be created inside the render function.
at <VueTurnstile ref="reCaptchaTurnstile" site-key="324324rsdxdfw3r4fd" modelValue="" ... >
And the error below shows this:
api.js?onload=cfTurnstileOnLoad&render=explicit:1 Uncaught (in promise) TurnstileError: [Cloudflare Turnstile] Invalid type for parameter "container", expected "string" or an implementation of "HTMLElement".
My component with the form is as follows which shows vue-turnstile integration at the end of the form:
<template>
<div class="text-center contact-us-form">
<h4 class="text-weight-bolder q-my-xs">GET A FREE QUOTE</h4>
<div class="text-subtitle1 text-accent q-mb-xl">
If you would like a quote or have any questions give us a call or send us
an email using the contact form!
</div>
<q-form @submit="onSubmit" @reset="onReset">
<div class="row q-col-gutter-md">
<div class="col-6">
<q-input
filled
v-model="firstName"
label="First Name"
hide-bottom-space
lazy-rules="ondemand"
:rules="[(val) => (val && val.length > 0) || 'First name required']"
/>
</div>
<div class="col-6">
<q-input
filled
v-model="lastName"
label="Last Name"
hide-bottom-space
lazy-rules="ondemand"
:rules="[(val) => (val && val.length > 0) || 'Last name required']"
/>
</div>
<div class="col-12">
<q-input
filled
v-model="phoneNumber"
type="tel"
mask="(###) ###-####"
label="Phone Number"
hide-bottom-space
lazy-rules="ondemand"
:rules="phoneNumberRules"
/>
</div>
<div class="col-12">
<q-input
filled
v-model="emailAddress"
type="email"
label="Email"
hide-bottom-space
lazy-rules="ondemand"
:rules="emailRules"
/>
</div>
<div class="col-12">
<q-select
filled
v-model="typeOfService"
:options="options"
label="Type of Service"
hide-bottom-space
lazy-rules="ondemand"
:rules="[
(val) => (val && val.length > 0) || 'Type of service required',
]"
/>
</div>
<div class="col-12">
<q-input
v-model="message"
filled
clearable
type="textarea"
label="Subject"
hide-bottom-space
lazy-rules="ondemand"
:rules="[(val) => (val && val.length > 0) || 'Message required']"
/>
</div>
<div class="col-12">
<q-btn
:loading="loading"
color="primary"
icon="send"
label="Send"
class="full-width q-pa-md"
unelevated
type="submit"
>
<template v-slot:loading>
<q-spinner-gears class="q-ma-sm" /> Sending...
</template>
</q-btn>
</div>
<div class="col-12">
<vue-turnstile
ref="reCaptchaTurnstile"
site-key="0x4AAAAAAAIGxTIQ6Z8JWGTo"
v-model="token"
/>
</div>
</div>
</q-form>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { useQuasar } from 'quasar';
import { api } from 'boot/axios';
import VueTurnstile from 'vue-turnstile';
//quasar variable
const $q = useQuasar();
//form inputs
const firstName = ref(null);
const lastName = ref(null);
const phoneNumber = ref(null);
const emailAddress = ref(null);
const typeOfService = ref(null);
const message = ref(null);
const token = ref('');
const reCaptchaTurnstile = ref(null); // we needed to define the ref for this so we could access reset()
const loading = ref(false);
// Drop down value for the services
const options = [
'Chimney Repair',
'Chimney Maintenance',
'Chimney Inspection',
'Other',
];
//form rules -- Added down here as reactive variables mainly due to complexity of their validation rules
const phoneNumberRules = ref([
(v) => !!v || 'Phone number is required',
(v) => v.length == 14 || 'Phone number must be valid',
]);
const emailRules = ref([
(v) => !!v || 'E-mail is required',
(v) =>
/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(v) ||
'E-mail must be valid',
]);
//methods
const onReset = () => {
firstName.value = null;
lastName.value = null;
phoneNumber.value = null;
emailAddress.value = null;
typeOfService.value = null;
message.value = null;
token.value = '';
reCaptchaTurnstile.value.reset();
};
const onSubmit = () => {
//start widget spinner and turn of when complete
loading.value = true;
let jsonData = {
FirstName: firstName.value,
LastName: lastName.value,
PhoneNumber: phoneNumber.value,
EmailAddress: emailAddress.value,
TypeOfService: typeOfService.value,
Message: message.value,
Token: token.value,
};
if (token.value != '') {
api
.post('/api/contactus', jsonData)
.then((response) => {
console.log(response);
//reset the form
onReset();
loading.value = false;
$q.notify({
color: 'green-4',
textColor: 'white',
icon: 'cloud_done',
message: 'Your message has been successfully sent!',
});
})
.catch(() => {
console.log('API request failed');
loading.value = false;
});
}
};
</script>
<style lang="scss" scoped>
#cf-chl-widget-mpzku {
width: 100% !important;
}
.contact-us-form {
max-width: 450px;
margin: auto;
}
</style>
I have tried deleting the package and reinstalling it. I cannot understand what this error even means. I really need to figure this out to stop spam and I have used this vue-turnstile in other apps that are still working so I am not sure why this wouldnt work for this app I am creating.
This is a common versioning issue and differences between yarn.lock, node module folders, and package.json. So run
rm -rf node_modules yarn.lock
yarn install