import { Input, OnInit, Component, ViewChild, ChangeDetectorRef, AfterViewInit, forwardRef, inject, } from "@angular/core"; import { ControlValueAccessor, FormControl, ValidatorFn, NG_VALUE_ACCESSOR, NG_VALIDATORS, } from "@angular/forms"; import { SamFormService, SamFormEvent } from "../../form-service"; import { LabelWrapper } from "../../wrappers/label-wrapper"; export function AccessorToken(className) { return { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => className), multi: true, }; } export function ValidatorToken(className) { return { provide: NG_VALIDATORS, useExisting: forwardRef(() => className), multi: true, }; } @Component({ template: "", standalone: false, }) export class SamFormControl implements ControlValueAccessor, OnInit, AfterViewInit { samFormService = inject(SamFormService); cdr = inject(ChangeDetectorRef); /** * Sets the label text */ @Input() public label: string; /** * Sets the name attribute on the input element */ @Input() public name: string; /** * Sets the id attribute on the input element, * this is linked with the label */ @Input() public id: string; /** * Sets the title attribute on the input element */ @Input() public title: string; /** * Sets the hint text under the label */ @Input() public hint: string; /** * (Deprecated) Sets the error message manually, * errors should be set in the the form controls directly * to populate error messages */ @Input() public errorMessage: string; /** * Sets the required snippet in the label */ @Input() public required: boolean; /** * Takes the form control to watch for changes to update * the label error messages */ @Input() public control: FormControl; /** * (Decprecated) Turns on the SamFormService in order to trigger * error message updates programmatically over value/status changes * in the form control */ @Input() public useFormService: boolean; /** * Enable to turn off all validations */ @Input() public disableValidation: boolean; @ViewChild(LabelWrapper, { static: true }) public wrapper: LabelWrapper; public defaultValidators: ValidatorFn[] = []; protected defaultValue: T = null; protected _value: T = null; protected _disabled: boolean; public onChange: (value?: T) => void = () => { return; }; public onTouched: () => void = () => { return; }; public get value(): T { return this._value; } public set value(val: T) { this._value = !val ? this.defaultValue : val; this.onChange(this.value); } public get disabled(): boolean { return this._disabled; } public set disabled(state: boolean) { this._disabled = state; } // Lifecycle Hooks public ngOnInit() { this.initReactiveForms(); } public ngAfterViewInit() { this.initWrapper(); } // ControlValueAccessor Methods public writeValue(val: T) { this.value = val; } public registerOnChange(fn: (value?: T) => void) { this.onChange = fn; } public registerOnTouched(fn: () => void) { this.onTouched = fn; } public setDisabledState(state: boolean) { this.disabled = state; } // Member methods private initReactiveForms() { if (this.control) { const validators: ValidatorFn[] = []; if (this.disableValidation) { if (this.control.validator) { validators.push(this.control.validator); } } else { this.defaultValidators.forEach((validator) => validators.push(validator) ); } this.control.setValidators(validators); this.setValidationMethod(); } } private setValidationMethod() { if (!this.useFormService) { this.control.statusChanges.subscribe( () => { this.wrapper.formatErrors(this.control); this.cdr.detectChanges(); }, () => console.error("Error occurred") ); } else { this.samFormService.formEventsUpdated$.subscribe( (evt: SamFormEvent) => { if ( (!evt.root || evt.root === this.control.root) && evt.eventType && evt.eventType === "submit" ) { this.wrapper.formatErrors(this.control); } else if ( (!evt.root || evt.root === this.control.root) && evt.eventType && evt.eventType === "reset" ) { this.wrapper.clearError(); } }, () => console.error("Error occured") ); } } private initWrapper() { if (this.control) { this.wrapper.formatErrors(this.control); this.cdr.detectChanges(); } } }