From 6663515d221c07f07575f77a0fa9542be6224862 Mon Sep 17 00:00:00 2001 From: Julien Minella Date: Mon, 23 Aug 2021 09:20:20 +0200 Subject: [PATCH] Add legacy support for Angular ValueAccessors * add legacyValueAccessors flag to OutputTargetAngular type (false by default to keep the behaviour consistent with the current build process) * add non extended value accessors for angular < 9 support * update value accessors generator script to handle legacy support --- .../boolean-value-accessor.ts | 53 ++++++++++++++++++ .../number-value-accessor.ts | 55 +++++++++++++++++++ .../radio-value-accessor.ts | 53 ++++++++++++++++++ .../select-value-accessor.ts | 53 ++++++++++++++++++ .../text-value-accessor.ts | 53 ++++++++++++++++++ .../control-value-accessors/value-accessor.ts | 6 +- .../src/generate-value-accessors.ts | 14 +++-- packages/angular-output-target/src/types.ts | 1 + 8 files changed, 280 insertions(+), 8 deletions(-) create mode 100644 packages/angular-output-target/resources/control-value-accessors-legacy/boolean-value-accessor.ts create mode 100644 packages/angular-output-target/resources/control-value-accessors-legacy/number-value-accessor.ts create mode 100644 packages/angular-output-target/resources/control-value-accessors-legacy/radio-value-accessor.ts create mode 100644 packages/angular-output-target/resources/control-value-accessors-legacy/select-value-accessor.ts create mode 100644 packages/angular-output-target/resources/control-value-accessors-legacy/text-value-accessor.ts diff --git a/packages/angular-output-target/resources/control-value-accessors-legacy/boolean-value-accessor.ts b/packages/angular-output-target/resources/control-value-accessors-legacy/boolean-value-accessor.ts new file mode 100644 index 00000000..4e221d9a --- /dev/null +++ b/packages/angular-output-target/resources/control-value-accessors-legacy/boolean-value-accessor.ts @@ -0,0 +1,53 @@ +import { Directive, ElementRef, HostListener } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; + +@Directive({ + /* tslint:disable-next-line:directive-selector */ + selector: '', + host: { + '()': 'handleChangeEvent($event.target.)' + }, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: BooleanValueAccessor, + multi: true + } + ] +}) +export class BooleanValueAccessor implements ControlValueAccessor { + + private lastValue: any; + private onChange: (value: any) => void = () => {/**/}; + private onTouched: () => void = () => {/**/}; + + constructor(protected el: ElementRef) {} + + writeValue(value: any) { + this.el.nativeElement.checked = this.lastValue = value == null ? false : value; + } + + handleChangeEvent(value: any) { + if (value !== this.lastValue) { + this.lastValue = value; + this.onChange(value); + } + } + + @HostListener('focusout') + _handleBlurEvent() { + this.onTouched(); + } + + registerOnChange(fn: (value: any) => void) { + this.onChange = fn; + } + + registerOnTouched(fn: () => void) { + this.onTouched = fn; + } + + setDisabledState(isDisabled: boolean) { + this.el.nativeElement.disabled = isDisabled; + } +} diff --git a/packages/angular-output-target/resources/control-value-accessors-legacy/number-value-accessor.ts b/packages/angular-output-target/resources/control-value-accessors-legacy/number-value-accessor.ts new file mode 100644 index 00000000..f575da55 --- /dev/null +++ b/packages/angular-output-target/resources/control-value-accessors-legacy/number-value-accessor.ts @@ -0,0 +1,55 @@ +import { Directive, ElementRef, HostListener } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; + +@Directive({ + /* tslint:disable-next-line:directive-selector */ + selector: '', + host: { + '()': 'handleChangeEvent($event.target.)' + }, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: NumericValueAccessor, + multi: true + } + ] +}) +export class NumericValueAccessor implements ControlValueAccessor { + + private lastValue: any; + private onChange: (value: any) => void = () => {/**/}; + private onTouched: () => void = () => {/**/}; + + constructor(protected el: ElementRef) {} + + writeValue(value: any) { + this.el.nativeElement.value = this.lastValue = value == null ? '' : value; + } + + handleChangeEvent(value: any) { + if (value !== this.lastValue) { + this.lastValue = value; + this.onChange(value); + } + } + + @HostListener('focusout') + _handleBlurEvent() { + this.onTouched(); + } + + registerOnChange(fn: (_: number | null) => void) { + this.onChange = value => { + fn(value === '' ? null : parseFloat(value)); + }; + } + + registerOnTouched(fn: () => void) { + this.onTouched = fn; + } + + setDisabledState(isDisabled: boolean) { + this.el.nativeElement.disabled = isDisabled; + } +} diff --git a/packages/angular-output-target/resources/control-value-accessors-legacy/radio-value-accessor.ts b/packages/angular-output-target/resources/control-value-accessors-legacy/radio-value-accessor.ts new file mode 100644 index 00000000..ee4fa61b --- /dev/null +++ b/packages/angular-output-target/resources/control-value-accessors-legacy/radio-value-accessor.ts @@ -0,0 +1,53 @@ +import { Directive, ElementRef, HostListener } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; + +@Directive({ + /* tslint:disable-next-line:directive-selector */ + selector: '', + host: { + '()': 'handleChangeEvent($event.target.)' + }, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: RadioValueAccessor, + multi: true + } + ] +}) +export class RadioValueAccessor implements ControlValueAccessor { + + private lastValue: any; + private onChange: (value: any) => void = () => {/**/}; + private onTouched: () => void = () => {/**/}; + + constructor(protected el: ElementRef) {} + + writeValue(value: any) { + this.el.nativeElement.value = this.lastValue = value == null ? '' : value; + } + + handleChangeEvent(value: any) { + if (value !== this.lastValue) { + this.lastValue = value; + this.onChange(value); + } + } + + @HostListener('focusout') + _handleBlurEvent() { + this.onTouched(); + } + + registerOnChange(fn: (value: any) => void) { + this.onChange = fn; + } + + registerOnTouched(fn: () => void) { + this.onTouched = fn; + } + + setDisabledState(isDisabled: boolean) { + this.el.nativeElement.disabled = isDisabled; + } +} diff --git a/packages/angular-output-target/resources/control-value-accessors-legacy/select-value-accessor.ts b/packages/angular-output-target/resources/control-value-accessors-legacy/select-value-accessor.ts new file mode 100644 index 00000000..3efcf4eb --- /dev/null +++ b/packages/angular-output-target/resources/control-value-accessors-legacy/select-value-accessor.ts @@ -0,0 +1,53 @@ +import { Directive, ElementRef, HostListener } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; + +@Directive({ + /* tslint:disable-next-line:directive-selector */ + selector: '', + host: { + '()': 'handleChangeEvent($event.target.)' + }, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: SelectValueAccessor, + multi: true + } + ] +}) +export class SelectValueAccessor implements ControlValueAccessor { + + private lastValue: any; + private onChange: (value: any) => void = () => {/**/}; + private onTouched: () => void = () => {/**/}; + + constructor(protected el: ElementRef) {} + + writeValue(value: any) { + this.el.nativeElement.value = this.lastValue = value == null ? '' : value; + } + + handleChangeEvent(value: any) { + if (value !== this.lastValue) { + this.lastValue = value; + this.onChange(value); + } + } + + @HostListener('focusout') + _handleBlurEvent() { + this.onTouched(); + } + + registerOnChange(fn: (value: any) => void) { + this.onChange = fn; + } + + registerOnTouched(fn: () => void) { + this.onTouched = fn; + } + + setDisabledState(isDisabled: boolean) { + this.el.nativeElement.disabled = isDisabled; + } +} diff --git a/packages/angular-output-target/resources/control-value-accessors-legacy/text-value-accessor.ts b/packages/angular-output-target/resources/control-value-accessors-legacy/text-value-accessor.ts new file mode 100644 index 00000000..4ffd1591 --- /dev/null +++ b/packages/angular-output-target/resources/control-value-accessors-legacy/text-value-accessor.ts @@ -0,0 +1,53 @@ +import { Directive, ElementRef, HostListener } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; + +@Directive({ + /* tslint:disable-next-line:directive-selector */ + selector: '', + host: { + '()': 'handleChangeEvent($event.target.)' + }, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: TextValueAccessor, + multi: true + } + ] +}) +export class TextValueAccessor implements ControlValueAccessor { + + private lastValue: any; + private onChange: (value: any) => void = () => {/**/}; + private onTouched: () => void = () => {/**/}; + + constructor(protected el: ElementRef) {} + + writeValue(value: any) { + this.el.nativeElement.value = this.lastValue = value == null ? '' : value; + } + + handleChangeEvent(value: any) { + if (value !== this.lastValue) { + this.lastValue = value; + this.onChange(value); + } + } + + @HostListener('focusout') + _handleBlurEvent() { + this.onTouched(); + } + + registerOnChange(fn: (value: any) => void) { + this.onChange = fn; + } + + registerOnTouched(fn: () => void) { + this.onTouched = fn; + } + + setDisabledState(isDisabled: boolean) { + this.el.nativeElement.disabled = isDisabled; + } +} diff --git a/packages/angular-output-target/resources/control-value-accessors/value-accessor.ts b/packages/angular-output-target/resources/control-value-accessors/value-accessor.ts index 6bb76a6d..a8673d17 100644 --- a/packages/angular-output-target/resources/control-value-accessors/value-accessor.ts +++ b/packages/angular-output-target/resources/control-value-accessors/value-accessor.ts @@ -1,12 +1,13 @@ import { Directive, ElementRef, HostListener } from '@angular/core'; import { ControlValueAccessor } from '@angular/forms'; -@Directive({}) +@Directive() export class ValueAccessor implements ControlValueAccessor { + protected lastValue: any; + private onChange: (value: any) => void = () => {/**/}; private onTouched: () => void = () => {/**/}; - protected lastValue: any; constructor(protected el: ElementRef) {} @@ -29,6 +30,7 @@ export class ValueAccessor implements ControlValueAccessor { registerOnChange(fn: (value: any) => void) { this.onChange = fn; } + registerOnTouched(fn: () => void) { this.onTouched = fn; } diff --git a/packages/angular-output-target/src/generate-value-accessors.ts b/packages/angular-output-target/src/generate-value-accessors.ts index 8e88b758..513172a0 100644 --- a/packages/angular-output-target/src/generate-value-accessors.ts +++ b/packages/angular-output-target/src/generate-value-accessors.ts @@ -25,6 +25,10 @@ export default async function generateValueAccessors( return; } + const sourcePath = + outputTarget.legacyValueAccessors !== true + ? '../resources/control-value-accessors/' + : '../resources/control-value-accessors-legacy/'; const targetDir = path.dirname(outputTarget.directivesProxyFile); const normalizedValueAccessors: NormalizedValueAccessors = outputTarget.valueAccessorConfigs.reduce( @@ -56,11 +60,7 @@ export default async function generateValueAccessors( const valueAccessorType = type as ValueAccessorTypes; // Object.keys converts to string const targetFileName = `${type}-value-accessor.ts`; const targetFilePath = path.join(targetDir, targetFileName); - const srcFilePath = path.join( - __dirname, - '../resources/control-value-accessors/', - targetFileName, - ); + const srcFilePath = path.join(__dirname, sourcePath, targetFileName); const srcFileContents = await compilerCtx.fs.readFile(srcFilePath); const finalText = createValueAccessor( @@ -71,7 +71,9 @@ export default async function generateValueAccessors( }), ); - await copyResources(config, ['value-accessor.ts'], targetDir); + if (outputTarget.legacyValueAccessors !== true) { + await copyResources(config, ['value-accessor.ts'], targetDir); + } } export function createValueAccessor(srcFileContents: string, valueAccessor: ValueAccessor) { diff --git a/packages/angular-output-target/src/types.ts b/packages/angular-output-target/src/types.ts index 374753b2..387c7b39 100644 --- a/packages/angular-output-target/src/types.ts +++ b/packages/angular-output-target/src/types.ts @@ -4,6 +4,7 @@ export interface OutputTargetAngular { directivesArrayFile?: string; directivesUtilsFile?: string; valueAccessorConfigs?: ValueAccessorConfig[]; + legacyValueAccessors?: boolean; excludeComponents?: string[]; }