Reputation: 261
Recently we were having some issue with our application signup part with OTP. we used firebase phone authentication it uses google reCaptcha v3. whenever we send the OTP it worked fine as expected but if we want to go back and change the number or in case of sending again OTP it was throwing below error.
reCaptcha has already been rendered on this element.
we've tried to clear the reCaptcha verifier but still nothing was working fine. I've worked on this issue before but was not in mind as i was focusing on other part of application.
Upvotes: 0
Views: 897
Reputation: 261
Basically when you use .clear()
method from reCaptcha verifier it will Clears the reCAPTCHA widget from the page and destroys the current instance. which means when you initialize the captcha on a dom element and after you run the clear()
method it will remove the captcha instance along with dom element as well. you can se below before and after change. Before when we were clearing the recaptcha it was removing the dom element and we could not re initialize as it won't get the element. Also if it was already initialized on a element you can't reinitialize until you clear.
signup.component.html :BEFORE
<div id="captcha-element"></div>
signup.component.html :AFTER
<div #captchaContainer>
<div id="captcha-element"></div>
</div>
signup.component.ts
declare var grecaptcha: any;
@Component({
selector: 'auth-signup',
templateUrl: './signup.component.html',
styleUrls: ['./signup.component.scss'],
})
export class LoginComponent implements OnDestroy {
otpIdentifier: string | null = null;
recaptchaVerifier: firebase.auth.RecaptchaVerifier | null = null;
recaptchaWidgetId: number | null = null;
@ViewChild('captchaContainer') captchaContainer!: ElementRef;
constructor() {}
async sendOtp() {
try {
if (!this.phoneNumber) {
return;
}
if (this.recaptchaVerifier && this.isSubmitting && !this.otpSent) {
//send otp here
}
} catch (error: any) {
console.error(error);
}
}
initiateRecaptchaContainer() {
this.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('captcha-element', {
'size': 'invisible',
'expired-callback': () => {
grecaptcha.reset(this.recaptchaWidgetId);
},
});
this.recaptchaVerifier?.render().then((id) => {
this.recaptchaWidgetId = id;
});
}
async resendOtp() {
this.clearRecaptcha();
this.initiateRecaptchaContainer();
if (this.recaptchaVerifier) {
//send otp here
}
}
clearRecaptcha() {
this.recaptchaVerifier?.clear();
this.captchaContainer.nativeElement.innerHTML = `<div id="captcha-element"></div>`;
}
returnAndReinitializeCaptcha() {
this.clearRecaptcha();
this.initiateRecaptchaContainer();
}
ngOnDestroy(): void {
}
ngAfterViewInit(): void {
this.initiateRecaptchaContainer();
}
}
Below change we made in component
//to clear the captcha and adding the element to dom again so that we can reinitialize the captcha.
@ViewChild('captchaContainer') captchaContainer!: ElementRef;
clearRecaptcha() {
this.recaptchaVerifier?.clear();
this.captchaContainer.nativeElement.innerHTML = `<div id="captcha-element"></div>`;
}
I hope it will solve your issue as well. You can use the same logic on any type of application where you find this type of issue.
Upvotes: 0