From 9e5c5e7e9e6a2929462d4b5e586aa9eb0bf38ad6 Mon Sep 17 00:00:00 2001 From: Ruslan Arkhipau Date: Thu, 22 Jul 2021 13:50:41 -0700 Subject: [PATCH] fix(component): reset form-bound captcha value after component destruction This is a back-port of 8.x.x fix for #201 --- .../recaptcha-value-accessor.directive.ts | 18 ++++++++++++++++++ src/recaptcha/recaptcha.component.ts | 14 ++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/recaptcha/recaptcha-value-accessor.directive.ts b/src/recaptcha/recaptcha-value-accessor.directive.ts index 4832834..9d25000 100644 --- a/src/recaptcha/recaptcha-value-accessor.directive.ts +++ b/src/recaptcha/recaptcha-value-accessor.directive.ts @@ -23,16 +23,34 @@ export class RecaptchaValueAccessorDirective implements ControlValueAccessor { /** @internal */ private onTouched: () => void; + private requiresControllerReset = false; + constructor(private host: RecaptchaComponent) {} public writeValue(value: string): void { if (!value) { this.host.reset(); + } else { + // In this case, it is most likely that a form controller has requested to write a specific value into the component. + // This isn't really a supported case - reCAPTCHA values are single-use, and, in a sense, readonly. + // What this means is that the form controller has recaptcha control state of X, while reCAPTCHA itself can't "restore" + // to that state. In order to make form controller aware of this discrepancy, and to fix the said misalignment, + // we'll be telling the controller to "reset" the value back to null. + if ( + this.host.__unsafe_widgetValue !== value && + Boolean(this.host.__unsafe_widgetValue) === false + ) { + this.requiresControllerReset = true; + } } } public registerOnChange(fn: (value: string) => void): void { this.onChange = fn; + if (this.requiresControllerReset) { + this.requiresControllerReset = false; + this.onChange(null); + } } public registerOnTouched(fn: () => void): void { this.onTouched = fn; diff --git a/src/recaptcha/recaptcha.component.ts b/src/recaptcha/recaptcha.component.ts index c832f34..0639465 100644 --- a/src/recaptcha/recaptcha.component.ts +++ b/src/recaptcha/recaptcha.component.ts @@ -122,6 +122,20 @@ export class RecaptchaComponent implements AfterViewInit, OnDestroy { } } + /** + * ⚠️ Warning! Use this property at your own risk! + * + * While this member is `public`, it is not a part of the component's public API. + * The semantic versioning guarantees _will not be honored_! Thus, you might find that this property behavior changes in incompatible ways in minor or even patch releases. + * You are **strongly advised** against using this property. + * Instead, use more idiomatic ways to get reCAPTCHA value, such as `resolved` EventEmitter, or form-bound methods (ngModel, formControl, and the likes).å + */ + public get __unsafe_widgetValue(): string | null { + return this.widget != null + ? this.grecaptcha.getResponse(this.widget) + : null; + } + /** @internal */ private expired() { this.resolved.emit(null);