How can I modify my custom Angular form control component based on validations?

120 Views Asked by At

I have a custom form control where I implemented the ControlValueAccessor interface, everything is working correctly. However, I want to make a change to the component based on validations, for example, I would like to add a marker to the '*' input label, in case the Validation.required was passed for the case of ReactiveForms or the required attribute was passed for the tag of the component in case of TemplateDrivenForms. Does anyone have any suggestions on how I can do this?

import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, forwardRef } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-input',
  standalone: true,
  imports: [CommonModule, FormsModule],
  templateUrl: './input.component.html',
  styleUrls: ['./input.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => InputComponent),
      multi: true
    }
  ]
})
export class InputComponent implements ControlValueAccessor, OnChanges {
  protected _value: any = '';
  protected _touched: boolean = false;
  protected _disabled: boolean = false;
  protected _onChange: any = () => {};
  protected _onTouch: any = () => {};

  @Input() type: string = 'text';
  @Input() label: string = '';

  writeValue(value: any): void {
    this._value = value;
  }

  registerOnChange(fn: any): void {
    this._onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this._onTouch = fn;
  }

  setDisabledState?(isDisabled: boolean): void {
    this._disabled = isDisabled;
  }

  onChange(value: any): void {
    if (!this._disabled) {
      this._onChange(value);
    }
  }

  onBlur(): void {
    if (!this._touched) {
      this._onTouch();
      this._touched = true;
    }
  }

}
2

There are 2 best solutions below

0
On

In a project of mine, I do it like this with TemplateDrivenForms:

   <form
    (ngSubmit)="onSubmit()"
    #form="ngForm"
   >
      <span
          [ngClass]="{
            '!text-red-400': !emailElement.valid && form.submitted
          }"
        >
          Email or Phone Number
        </span>
        <span
          *ngIf="emailElement.valid || !form.submitted"
          >
           *
         </span
        >
    </form>

You can do the same in TS by subscribing to form.valueChanges (or specific values of the form). By reading the docs or debugging you can see the different observables that form gives you.

1
On

you can show the asterix with the help of NgControl and control.hasValidator method (and remove the providers array)

  showAsterix: boolean;

   constructor(@Self() @Optional() private control: NgControl) {
     this.control.valueAccessor = this;
   }

  ngOnInit(): void {
    this.showAsterix = this.ngControl.control?.hasValidator(Validators.required);
  }

then in your template you can use showAsterix variable