Reputation: 2610
So I followed a tutorial on how to add recaptcha to a form to prevent spam. I followed all the steps, but I still get the following error:
error inside log Error: No value accessor for form control with name: 'captcha'
at _throwError (forms.js:2139)
at setUpControl (forms.js:2008)
at FormGroupDirective.addControl (forms.js:5144)
at FormControlName._setUpControl (forms.js:5743)
at FormControlName.ngOnChanges (forms.js:5684)
at checkAndUpdateDirectiveInline (core.js:11642)
at checkAndUpdateNodeInline (core.js:13252)
I have a reactive signup form where I have the following element that's supposed to be the captcha and a formControl. BUt unfortunately I see nothing.
<div nbRecaptcha key="6LfNfHYUAAAAAFaa7dVi2au1tnfod-2eR3Rb2enM" formControlName="captcha"></div>
This is the nbRecaptcha directive:
declare const grecaptcha: any;
declare global {
interface Window {
grecaptcha: any;
reCaptchaLoad: () => void
}
}
@Directive({
selector: '[nbRecaptcha]',
providers: [ReCaptchaAsyncValidator]
})
export class RecaptchaDirective implements OnInit, ControlValueAccessor, AfterViewInit {
@Input() key: string;
@Input() config: ReCaptchaConfig = {};
@Input() lang: string;
private control: FormControl;
private onChange: ( value: string ) => void;
private onTouched: ( value: string ) => void;
private widgetId: number;
constructor(
private element: ElementRef,
private ngZone: NgZone,
private injector: Injector,
private reCaptchaAsyncValidator: ReCaptchaAsyncValidator){}
ngOnInit() {
this.registerReCaptchaCallback();
this.addScript();
}
ngAfterViewInit() {
this.control = this.injector.get(NgControl).control;
this.setValidator();
}
addScript() {
let script = document.createElement('script');
const lang = this.lang ? '&hl=' + this.lang : '';
script.src = `https://www.google.com/recaptcha/api.js?onload=reCaptchaLoad&render=explicit${lang}`;
script.async = true;
script.defer = true;
document.body.appendChild(script);
}
// We need to notify the formControl that it’s valid if we get the token
// from the onSuccess function or that it’s invalid if the onExpired function is called.
onExpired() {
this.ngZone.run(() => {
this.onChange(null);
this.onTouched(null);
});
}
onSuccess( token: string ) {
this.ngZone.run(() => {
this.verifyToken(token);
this.onChange(token);
this.onTouched(token);
});
}
// these are the three methods that controlValueAccessor requires
writeValue( obj: any ): void {
}
registerOnChange( fn: any ): void {
this.onChange = fn;
}
registerOnTouched( fn: any ): void {
this.onTouched = fn;
}
private setValidator() {
this.control.setValidators(Validators.required);
this.control.updateValueAndValidity();
}
registerReCaptchaCallback() {
window.reCaptchaLoad = () => {
const config = {
...this.config,
'sitekey': this.key,
'callback': this.onSuccess.bind(this),
'expired-callback': this.onExpired.bind(this)
};
this.widgetId = this.render(this.element.nativeElement, config);
};
}
private render( element: HTMLElement, config ): number {
return grecaptcha.render(element, config);
}
verifyToken( token : string ) {
this.control.setAsyncValidators(this.reCaptchaAsyncValidator.validateToken(token))
this.control.updateValueAndValidity();
}
}
And the signup component form:
this.mySignupForm = new FormGroup({
captcha: new FormControl()
});
Can Anyone see what I did wrong?
Upvotes: 2
Views: 3291
Reputation: 862
Sometimes we get this kind of error when import it in app.module instead of the own module where we need to use . for example
///app.module.ts
import { RecaptchaModule, RecaptchaFormsModule } from "ng-recaptcha";
imports: [
BrowserModule,
RecaptchaModule,
RecaptchaFormsModule
]
move it to --->
///x.module.ts
import { RecaptchaModule, RecaptchaFormsModule } from "ng-recaptcha";
@NgModule({
imports: [
FormsModule,
RecaptchaModule,
RecaptchaFormsModule,
],
providers:[
{
provide: RECAPTCHA_SETTINGS,
useValue: { siteKey: environment.recaptcha.siteKey } as RecaptchaSettings,
},
]
Upvotes: 0
Reputation: 38837
The error No value accessor for form control with name: 'captcha'
. This can be resolved by adding the directive/control to list of all available NG_VALUE_ACCESSOR
s using forwardRef
. You are effectively extending the multi-provider for NG_VALUE_ACCESSOR
so that this directive can access ControlValueAccessor
.
@Directive({
selector: '[nbRecaptcha]',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => RecaptchaDirective),
multi: true
},
ReCaptchaAsyncValidator
]
})
Here is a mostly working example with that specific error being resolved. I had to remove some of the custom classes/configurations you had in your example as I wasn't aware of their exact implementations.
I could not find official documentation for creating custom controls, but there are a few solid articles out there discussing creating custom controls that reference this necessary registration.
Hopefully that helps!
Upvotes: 2