custom form control does not trigger any validation using NgControl and addValidators Function

1.6k Views Asked by At

I'm trying to learn about different ways to create custom form control using angular 13.

I've watched the Angular Connect 2017 video regarding angular forms from https://www.youtube.com/watch?v=CD_t3m2WMM8 and tried to implement and custom form control with validators.

i have a form with email and password, i created a custom control for the email, and none of the validations that i configured are being triggered (required and email)

the custom form element is called app-tuxin-form-email-input that contains it's own form using a form builder.

this is the class:

import {Component, OnInit, Self, SimpleChanges} from '@angular/core';
import {
  ControlValueAccessor, FormBuilder, FormGroup,
   NgControl,
  Validators
} from '@angular/forms';

@Component({
  selector: 'app-tuxin-form-email-input',
  templateUrl: './tuxin-form-email-input.component.html',
  styleUrls: ['./tuxin-form-email-input.component.scss'],
})
export class TuxinFormEmailInputComponent implements ControlValueAccessor, OnInit {

  newChildForm: FormGroup;

  onChange = (email: string) => {};

  onTouched = () => {};

  touched = false;

  disabled = false;

  constructor(private fb: FormBuilder, @Self() public controlDir: NgControl) {
    controlDir.valueAccessor=this;
    this.newChildForm = this.fb.group({
      email: [''],
    });
  }

  writeValue(email: string) {
    this.newChildForm.get('email')?.setValue(email, { emitEvent: false });
  }

  registerOnChange(onChange: any) {
    this.onChange = onChange;
  }

  registerOnTouched(onTouched: any) {
    this.onTouched = onTouched;
  }

  ngOnInit(): void {
    const control = this.controlDir.control;
    const validators = [Validators.required, Validators.email];
    if (control) {
      if (control.validator) {
        validators.unshift(control.validator);
      }
      control.setValidators(validators);
      control.updateValueAndValidity();
    }
  }

  setDisabledState(disabled: boolean) {
    this.disabled = disabled;
    disabled ? this.newChildForm.disable() : this.newChildForm.enable();
  }

}

and this is the template:

<div [formGroup]="newChildForm">
<mat-form-field>
  <mat-label>Email</mat-label>
  <input matInput type="email" formControlName="email"
  (blur)="onTouched()" (input)="onChange($any($event.target).value)" [disabled]="disabled"
  />
  <mat-error i18n *ngIf="controlDir && controlDir.hasError('required')">Email is required</mat-error>
  <mat-error i18n *ngIf="controlDir && controlDir.hasError('email')">Email Invalid</mat-error>
</mat-form-field>
</div>

so unfortunately none of the validators errors are being triggers and i can't understand why.

I created a stackblitz for that at https://stackblitz.com/edit/angular-ivy-2s36qf?file=src/app/app.module.ts

any ideas regarding this issue would be greatly apprecaited.

thanks

1

There are 1 best solutions below

2
On

Using the FormControl object on your child component, and it makes it easier to use NG_VALUE_ACCESSOR when you do so.

Also the registration of events have a callback which needs to be called for the outer form to be notified of the form changes

I forked your stackblitz and adjusted it accordingly.

The main changes were to:

  1. add the forwardRef:
@Component({
  selector: 'app-tuxin-form-email-input',
  templateUrl: './my-comp.html',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => TuxinFormEmailInputComponent),
      multi: true,
    },
  ],
})
export class TuxinFormEmailInputComponent
  1. to have the formControl for the email available as a variable

  2. and to adjust callbacks to the event registration methods registerOnChange and registerOnTouched