Reputation: 85
So I had an issue with svelte and google Recaptcha API.
My main HTML file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Svelte app</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel='stylesheet' href='/global.css'>
<link rel='stylesheet' href='/build/bundle.css'>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Bitter:400,700">
<link rel="stylesheet" href="/css/styles.min.css">
<script defer src='/build/bundle.js'></script>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@9"></script>
</head>
<body>
</body>
</html>
My main.svelte
<script>
import swal from "sweetalert";
function verifyUser() {
swal({
title: "Please wait a minute!",
text: "Do not close or exit this tab, we are currently verifying you...",
icon: "info",
backdrop: `rgba(0,0,0,1)`,
showConfirmButton: false,
allowOutsideClick: false,
allowEscapeKey: false
});
}
</script>
<div>
<div class="header-dark">
<nav class="navbar navbar-dark navbar-expand-lg navigation-clean-search">
<div class="container">
<a class="navbar-brand" href="/">QSP Human Verification Module</a>
<button class="navbar-toggler" data-toggle="collapse">
<span class="sr-only">Toggle navigation</span>
<span class="navbar-toggler-icon" />
</button>
</div>
</nav>
<div class="container hero">
<div class="row">
<div class="col-md-8 offset-md-2">
<h1 class="text-center">
Please complete the Captcha challenge to continue to the server.
</h1>
<form action="" method="post">
<div
class="g-recaptcha"
data-sitekey="key"
data-callback={verifyUser}
/>
</form>
</div>
</div>
</div>
</div>
</div>
Problem is that function just becomes string of itself and can't be executed using just "verifyUser" or
verifyUser()returns
ReCAPTCHA couldn't find user-provided function: verifyUser`
using {verifyUser}
becomes string (for more https://prnt.sc/qhyc2w)
executing like that returns:
ReCAPTCHA couldn't find user-provided function: function verifyUser() {
swal({
title: "Please wait a minute!",
text: "Do not close or exit this tab, we are currently verifying you...",
icon: "info",
backdrop: `rgba(0,0,0,1)`,
showConfirmButton: false,
allowOutsideClick: false,
allowEscapeKey: false
});
}
rollup config
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
const production = !process.env.ROLLUP_WATCH;
export default {
input: 'src/main.js',
output: {
sourcemap: true,
format: 'iife',
name: 'app',
file: 'public/build/bundle.js'
},
plugins: [
svelte({
// enable run-time checks when not in production
dev: !production,
// we'll extract any component CSS out into
// a separate file — better for performance
css: css => {
css.write('public/build/bundle.css');
}
}),
// If you have external dependencies installed from
// npm, you'll most likely need these plugins. In
// some cases you'll need additional configuration —
// consult the documentation for details:
// https://github.com/rollup/plugins/tree/master/packages/commonjs
resolve({
browser: true,
dedupe: importee => importee === 'svelte' || importee.startsWith('svelte/')
}),
commonjs(),
// In dev mode, call `npm run start` once
// the bundle has been generated
!production && serve(),
// Watch the `public` directory and refresh the
// browser on changes when not in production
!production && livereload('public'),
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser()
],
watch: {
clearScreen: false
}
};
function serve() {
let started = false;
return {
writeBundle() {
if (!started) {
started = true;
require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
stdio: ['ignore', 'inherit', 'inherit'],
shell: true
});
}
}
};
}
Any how to properly pass the function?
Upvotes: 4
Views: 4027
Reputation: 1117
If using TypeScript, you'll need to define your function for the Window interface.
global.d.ts
declare interface Window {
onSubmit: (token) => void,
}
RecaptchaButton.svelte
<script lang="ts">
import { createEventDispatcher, onMount, onDestroy } from 'svelte';
import { variables } from '../../variables';
const { recaptchaSiteKey } = variables;
const dispatch = createEventDispatcher();
type ButtonType = 'primary' | 'secondary';
export let type: ButtonType = 'primary';
function onSubmit(token) {
dispatch('click', { token });
}
onMount(() => {
window.onSubmit = onSubmit;
});
onDestroy(() => {
window.onSubmit = null;
});
</script>
<svelte:head>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
</svelte:head>
<form on:submit|preventDefault={onSubmit}>
<button
class="g-recaptcha {type}"
data-sitekey={recaptchaSiteKey}
data-callback="onSubmit"
type="submit"
>
Submit
</button>
</form>
<style>
button {
...
}
...
</style>
ContactForm.svelte
<script>
function handleSubmit(event) {
console.log(event.detail.token);
return;
}
</script>
<RecaptchaButton on:click={handleSubmit} type="primary">Submit</ButtonRecaptcha>
You should add an error handler to the Recaptcha button component if using something like the above, you can use the "data-error-callback" property on the buttton; set up the Window interface, mount and destroy the same as "onSubmit" but with "onError".
Upvotes: 1
Reputation: 1280
You can try svelte-recaptcha-v2
package on npm for a more straight forward experience.
https://github.com/basaran/svelte-recaptcha-v2
https://basaran.github.io/svelte-recaptcha-v2/
P.S I'm the author of the package.
Upvotes: 2
Reputation: 126
im Rour- the guy from discord. I want to share solution here for others.
<script>
import { onMount, onDestroy } from 'svelte';
function verifyUser() {/* your fun here */}
onMount(() => {
window.verifyUser = verifyUser;
})
onDestroy(() => {
window.verifyUser = null;
})
</script>
<!-- then pass the name of function 'verifyUser' just as string' -->
<div class="g-recaptcha" data-sitekey="key" data-callback="verifyUser" />
Thinking that data-callback
is searching for name of global function, so we define verifyUser
as global function in the browser by assign it to the window
variable
Upvotes: 6