代码之家  ›  专栏  ›  技术社区  ›  tilly

角度:尝试添加recaptcha。错误=>没有名为“captcha”的窗体控件的值访问器

  •  1
  • tilly  · 技术社区  · 7 年前

    因此,我遵循了一个关于如何将recaptcha添加到表单以防止垃圾邮件的教程。我遵循了所有步骤,但仍然出现以下错误:

     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)
    

    我有一个反应式注册表单,其中我有下面的元素,应该是captcha和formControl。但不幸的是我什么也没看到。

    <div nbRecaptcha key="6LfNfHYUAAAAAFaa7dVi2au1tnfod-2eR3Rb2enM" formControlName="captcha"></div>
    

    这是nbRecaptcha指令:

    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();
      }
    }
    

    以及注册组件表单:

    this.mySignupForm = new FormGroup({
              captcha: new FormControl()
            });
    

    有人能看出我做错了什么吗?

    1 回复  |  直到 7 年前
        1
  •  2
  •   Alexander Staroselsky    7 年前

    No value accessor for form control with name: 'captcha' . 这可以通过将指令/控件添加到所有可用的 NG_VALUE_ACCESSOR 正在使用 forwardRef . 您有效地扩展了 NG\ U值\存取器 以便此指令可以访问 ControlValueAccessor .

    @Directive({
      selector: '[nbRecaptcha]',
      providers: [
        {
          provide: NG_VALUE_ACCESSOR,
          useExisting: forwardRef(() => RecaptchaDirective),
          multi: true
        },
        ReCaptchaAsyncValidator
      ]
    })
    

    example 解决了那个特定的错误。我不得不删除示例中的一些自定义类/配置,因为我不知道它们的具体实现。

    我找不到正式的 documentation 对于创建自定义控件,有一些文章讨论了如何创建引用此必要注册的自定义控件。

    希望这有帮助!