-
Notifications
You must be signed in to change notification settings - Fork 122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Best way to use ng-recaptcha with multiple forms #35
Comments
Hi, @Toub ! As to the error that you mentioned - it's very hard to navigate through webpack-uglified vendor bundle, could you provide a non-obfuscated stack trace for further investigation? As to how you can solve the issue. Since you already have the this.recaptchaRef.resolved.subscribe((response) => /* stuff */);
this.recaptchaRef.execute(); Would that suffice? |
Labelling this as |
This error only occurs sometimes in production, so I can't get a better stacktrace. I tried locally your solution, and it looks good, so I will deploy in to production and let you know if it works. If yes, it could be good to document the solution. Here is what I did: Main component <!-- AccountLogin.html -->
<!-- declare captcha -->
<re-captcha #recaptchaRef="reCaptcha" size="invisible" siteKey="***"></re-captcha>
<!-- pass a reference to sub-components -->
<tw-account-reset-form [recaptchaRef]="recaptchaRef"></tw-account-reset-form>
<tw-account-create-form [recaptchaRef]="recaptchaRef"></tw-account-create-form> // AccountLogin.ts
import {RecaptchaComponent} from 'ng-recaptcha';
export class AccountLogin {
@ViewChild('recaptchaRef')
recaptchaRef: RecaptchaComponent; Sub form 1 & 2 // AccountResetForm.ts
import {RecaptchaComponent} from 'ng-recaptcha';
export class AccountResetForm {
@Input()
recaptchaRef: RecaptchaComponent;
captchaResolved(){
// form submitted, do something
}
tryToSubmit() {
this.recaptchaRef.resolved.first().subscribe((captchaResponse) => this.captchaResolved(captchaResponse), err => {
console.log('[AccountSigninEmail] Error resolving captcha', err);
}, () => {
// finally
this.recaptchaRef.reset();
});
this.recaptchaRef.execute()
}
} I guess it is important to subscribe to the first emitted item only, and also to reset the component in the final block. Also, when the first visible captcha appeared, I got the following error locally (not sure it can help!): TypeError: a.la is null
Vp()
recaptcha__fr.js:393
Np.prototype.Hk()
recaptcha__fr.js:394
[1781]/</</ZoneDelegate.prototype.invokeTask()
polyfills.bundle.js:7597
[1781]/</</Zone.prototype.runTask()
polyfills.bundle.js:7364
ZoneTask/this.invoke()
polyfills.bundle.js:7659
timer() But there is no side-effect for the user, as the captcha shows and works correctly. |
@Toub could you please re-verify the issue? It could have been resolved by a recaptcha update, as mentioned in #38 (comment) |
I updated and published to prod, but I still see the following issue in the logs reported by sentry tracker (not sure if it is related to the fact I have multiple forms):
It could also be related to a network problem loading google captcha itself. |
@Toub it looks like reCAPTCHA is being loaded successfully. I assume that you are right, and this error indeed appears due to the fact that there are multiple reCAPTCHA components on a page. reCAPTCHA seems to have problems with such a pattern. Since you haven't been able to reproduce the issue in DEV environment it's hard to tell whether it has an impact on the end user or not. Unfortunately, there's little I can do at this point to help mitigate this. My suggestion would be to refactor two forms in such a way that there is at maximum one recaptcha shown. |
Hello, I have the same problem with the two forms and the solution that implements the next, to see what you think. // service.ts
@Injectable()
export class RecaptchaService {
private recaptchaSource = new BehaviorSubject<Boolean>(false);
getStatus = this.recaptchaSource.asObservable();
constructor() {}
public setStatus(option: Boolean) {
this.recaptchaSource.next(option);
}
} // child component.ts
@Output() submit: EventEmitter<any> = new EventEmitter<any>(); // parent component.ts
@ViewChild('recaptchaRef') recaptchaRef: RecaptchaComponent;
recaptchaStatus: Boolean = false;
constructor(private recaptchaService: RecaptchaService) { }
ngOnInit() {
//subscribe to recaptcha service
this.recaptchaService.getStatus.subscribe(status => {
this.recaptchaStatus = status;
console.log(status);
});
}
ngAfterViewInit() {
console.log(`ngAfterViewInit`, this.recaptchaRef);
//if recaptcha is resolved then send HTTP request!
this.recaptchaRef.resolved.first().subscribe(
captchaResponse => {
this.recaptchaService.setStatus(true);
console.log(`Captcha valid, sending HTTP request...`);
},
err => {
console.log('[AccountSigninEmail] Error resolving captcha', err);
//set false to recaptcha status
this.recaptchaService.setStatus(false);
},
() => {
//finally reset reCaptcha
this.recaptchaRef.reset();
}
);
}
onSubmit(event) {
//on Submit execute recaptchaRef
this.recaptchaRef.execute();
} |
@Bodeclas hi, did you manage to solve it? |
@ch-hristov hi! yes, I did. |
recaptcha.service.ts import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class RecaptchaService {
private recaptchaSource = new BehaviorSubject<Boolean>(false);
getStatus = this.recaptchaSource.asObservable();
constructor() { }
public setStatus(option: Boolean) {
this.recaptchaSource.next(option);
}
} sign-up-layout.html <mat-tab-group>
<mat-tab>
<app-form-user></app-form-user>
</mat-tab>
<mat-tab>
<app-form-company></app-form-company>
</mat-tab>
</mat-tab-group>
<!-- ReCaptcha invisible -->
<re-captcha size="invisible" #recaptchaRef="reCaptcha"></re-captcha> import { RecaptchaComponent } from 'ng-recaptcha';
import { RecaptchaService } from '@core/services';
@ViewChild('recaptchaRef') recaptchaRef: RecaptchaComponent;
...
class SignUpLayout
...
ngAfterViewInit() {
this.recaptchaRef.resolved.subscribe((response) => {
// Captcha valid
this.recaptchaService.setStatus(true);
}, error => {
console.error('[AccountSigninEmail] Error resolving captcha');
this.recaptchaService.setStatus(false);
this.recaptchaRef.reset();
});
}
onSubmit(event) {
console.log(event);
if (!this.recaptchaStatus) {
// If recatpcha status is not resolved then execute recaptchaRef
this.recaptchaRef.execute();
}
}
... |
Hi have 2 components containing form on the same page, and I want to use invisible recaptcha in both of them.
I tried to use it twice, one recaptcha per component, and it looks to work almost, but I frequently get this JS error:
So, I guess it is probably better to create the recaptcha in my main component, then get a reference to it using @ViewChild, and pass it to each of my sub-components as an Input().
And when submitting a form in one of my component, manually trigger:
But infortunately, this method does not return an observable nor provide any callback mechanism, and make it tricky to execute the form action in my sub-component once the captcha has been resolved.
Any suggestion to solve my issue, or any plan to support such a scenario?
The text was updated successfully, but these errors were encountered: