ng-recaptcha: ng-recaptcha tag causes error “zone.js: Unhandled Promise rejection” when route changes
[Maintainer update 2022-09-26]
Please see this comment.
Summary
I’m submitting a:
- bug report
Description
I’m using “ng-recaptcha” on Angular component. Im using inside a form like this:
<form class="form-contact"
[formGroup]="contactForm"
#formDirective="ngForm"
(ngSubmit)="sendMessage(contactForm, formDirective)">
<re-captcha class="recaptcha"
formControlName="captcha"
siteKey="mysitekey">
</re-captcha>
</form>
Everything seems to be working fine. However, when route changes (ex. user goes to another page) and the AboutComponent is rendered again, an error pops up (multiple times): Unhandled Promise rejection: timeout ; Zone: <root> ; Task: Promise.then ; Value: timeout undefined. I’m 99% sure it’s caused by <re-captcha> tag because error doesn’t show up after removing the tag. Is there a way to render a captcha without errors when route changes (and captcha reloads after im back to component)? Obs: You must wait a few seconds after navigating to another route and back to captcha, then Promise timeout expires and error shows up inside console (google/firefox)
- I did my best to come up with a Minimal, Complete, and Verifiable example
https://stackblitz.com/edit/angular-en3spw
Lib versions:
- ng-recaptcha: 4.0.0-beta.1
- Angular: 6.1.4
- Typescript (
tsc --version
): 2.9.2
About this issue
- Original URL
- State: open
- Created 6 years ago
- Reactions: 38
- Comments: 47 (5 by maintainers)
To not keep you folks in the dark, here’s a small status update.
Why it happens?
This error happens due to the following scenario:
ng-recaptcha
invokes thegrecaptcha.reset(ID)
methodapi2/anchor
endpoint that @BrentACole mentionsrecaptcha__en.js
file there’s no way we could catch it (e.g. patching this file is not an option given such a sophisticated minimization algorithm used)Why not just skip invoking
grecaptcha.reset
duringngOnDestroy
?It’s there for a reason, and this reason is #10 .
What can I do?
If you can - ignore it. Add this error to an allowlist in your e2e tests or frontend monitoring software.
Otherwise, you can override the
ngOnDestroy
method ofRecaptchaComponent
manually (or create a component fork). Please be aware, that this is a temporary and pretty bad fix that might put you in a very unfortunate position when upgrading component versions! Nevertheless, here’s approximate code for that:What next?
I’ve submitted this issue to the recaptcha support team, and I’ll post an update here when I have it.
Thx for bearing with me on that!
Anyone solved this error ? I had the same error on angular 4 with recaptcha.
Does anyone know of a work around with this?
@SirWojtek the recaptcha core team does not have a public bug tracker (at least not one that I know of). I’ve submitted an issue to their support team through email, and I followed up again today. First time they recommended me to submit a question to StackOverflow, which I did with no results so far.
Let’s see if they get back with some hints/workarounds/timelines anytime soon. I would be very happy to bring you the good news today, but right now this is all I’ve got
Hey folks! Thx for letting me know about this, and I’m sorry that getting back to you took so long.
At this point I think I know where the issue is, ~and I plan to release a fix for it in the next couple of days in the 4.x.x branch.~ and I’ve reached out to recaptcha support team, see my comment below
This issue still exists in “ng-recaptcha”: “^10.0.0” is there any fix available ??
While I hope the above suggestion helps someone, I would like to point out few disadvantages to consider while using it:
Hi all!
Last week, I also ran into this problem myself. I found that destroying, then re-instantiating the ng-recaptcha component between route changes may be the cause of this bug, as the newly instantiated component is not able to handle the promise when the recaptcha expires, and the resolved(response: string) method is being called back. The workaround I found for this, is that I added the ng-recaptcha component to the root component of my application like so:
app.component.html:
Then I implemented a service for offering the token as an observable to components in which I’m using recaptcha, so they can subscibe to the event:
captchav2.service.ts:
To wire the service with the actual callback method:
app.component.ts
Then in the component, in which I want to use recaptcha (in my case, it is a component for registration):
register.component.ts:
So my solution works by having a single instance of the ng-recaptcha component throughout the entire application. In my case, it’s good enough. This solution probably won’t work properly if you need to have multiple instances of the ng-recaptcha component for whatever reason.
I advise you to have the badge hidden by default:
Then, you can either un-hide the badge on the pages you want to use it (this can be tricky!), or just include the text suggested by google on the page:
Note, that I’m not legally responsibe for the validity of this information, so you better check out Google’s the terms on this matter
What do you think of that? If you see any downsides of this solution, please let me know.
This issue still exists in “11.0.0”…
Wanted to add a couple notes to the group. I spent a good deal of time working on this problem, looking at alternative libraries on github, and going so far as to even write my own component to take a fresh perspective. Long story short, I think the ng-recaptcha library is good.
A couple notes…
This error is unavoidable from best I can tell, and similar to the authors original notes it appears to be on Google’s end. They don’t have a dispose option, which makes things very difficult.
You can override the dispose method using RecaptchaComponent.prototype.ngOnDestroy as mentioned in the original answer by the author, but I would strongly discourage this at this point. When going this route, you will remove the cancel error caused when calling google’s recaptcha service and switching routes mid-stream, and you will also not get an unhandled promise rejection error. However, working on the latest version of Angular, I noticed after a longer period of time I would get Timeout errors with this method of solution, and these would persist. The reason the other errors go away is you are no longer destroying the iframes created by Google’s recaptcha. In other words you create a new more persistent error, and you make the DOM busier. This method may have worked in the past, but I wouldn’t recommend it today.
I’ve got an improved workaround solution to the problem I think, but still need to do some final testing. I’ll post an update here shortly in the next day or two with an update.
I am still testing, but this seemed to work for me.
Then after the form is submitted, I do
this.captchaEl.reset();
again. Still testing, but I am not seeing the error anymore.Hey @DethAriel, can you paste the link to the
recapcha
issue you created?In search of a solution to the error, I noticed that by adding the following entry to the component, the error disappears. I’m using Angular v12 and ng-recaptcha v8
The answer posted by @gyomi95 is what I was planning to do as well, got busy last week on other projects. Basically the issue is not easy to fix until Google deals with it, so the best path is to create it at the root so its not destroyed and you can reuse it across components.
I had someone Ina discord give me something like this which works most of the time but sometimes will still throw errors.
I had the same error message, but in my case captcha doesn’t load anymore …
I have 2 forms with captcha, first time the captcha render properly, but when i navigate between them captcha doesn’t load anymore.
Only workaround i’ve found : on form component destroy, remove the captcha element from DOM
Now captcha always load properly, but error message continue to show up…
It’s not necessary to reload a component to get this error - destroying a component containing recaptcha leading to this case PS. This problem is also present in version 3.0.5 of ng-recaptcha