angular material custom form control component with custom validation doesn't trigger on form submit

2k Views Asked by At

i have implemented a custom form control with custom validation but i can't manage to mark the control as invalid once the parent form is submitted i want that when user press submit the control gets invalidated.. so far it gets invalidated only when touching it.. in my onDoCheck i manage to read the form.isSubmitted property but i noticed that the control is still marked as "valid"

even if i build the logic and force it as invalid in my onDoChek.. that way in the parent component on submit i can't check for the control validity because i would set it after the onSubmit callback.. what am i missing here?

the ugly fix i can think of is adding

this.form.get('test').updateValueAndValidity();
this.form.get('test').markAsTouched(); 

on the onSubmit callback, but shouldn't it work out of the box without doing that?

here the stackblitz: https://stackblitz.com/edit/angular-k5un7v?file=src%2Fapp%2Fcustom-input%2Fcustom-input.component.ts

any help is greatly appreciated

4

There are 4 best solutions below

1
On BEST ANSWER

Even my solution is not perfect I would suggest you to add NG_VALIDATORS in the providers of your component.

Currently your form is valid when the application starts and it should not. In fact, if I run your example following properties are wrongly true :

Custom input valid: {{form.get('test').valid}} 
Form valid: {{form.valid}}

When I add NG_VALIDATORS in providers of your custom CVA component, these properties become false, as they should:

{
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => CustomInputComponent),
      multi: true
},

Just adding these lines, your form and your custom field are now invalid when the app starts, since you already have added a validate() method in your component.

Anyway the mat-error still doesn't show the error because of the Material component behaviors. From doc:

mat-error: Errors are hidden initially and will be displayed on invalid form fields after the user has interacted with the element or the parent form has been submitted. Source: https://material.angular.io/components/form-field/overview

I don't know if you can easily change this behavior but now you can create your own styles and the form should properly work fine. Or, as other people told you, mark them as touched after submit.

I hope this advice can help you to find another way to fix your problem.

Here my StackBlitz: https://stackblitz.com/edit/angular-vgyug5?file=src%2Fapp%2Fapp.component.html

1
On

Before submitting and checking for validating run markAllAsTouched() on the form.

0
On

for the ones interested, i kind of found a better solution in which i don't require to mess with the form in the parent component

https://stackblitz.com/edit/angular-7gmzya?file=src%2Fapp%2Fcustom-input%2Fcustom-input.component.ts

basically all i had to do is

 ngAfterViewInit() {
    //console.log("IS REQUIRED? ", this.required)
    if (this.required && this.ngControl && this.ngControl.control) {
      this.ngControl.control.setValidators(this.validate);
      //UPDATING VALIDITY:
      setTimeout(()=> {
       this.ngControl.control.updateValueAndValidity();
      }
      );
    }
  }

and in ngDoCheck this:

this.errorState = this.ngControl.invalid && (this.ngControl.touched);

is now:

this.errorState = this.ngControl.invalid && (this.ngControl.touched || this.form.submitted);

this way the component behaves just like a standard angular form control, yet the solution it's not perfect i think because i had to resort to a setTimeout, so i'm leaving this as unresolved if you guys got a more elegant solution..

1
On

I think your issue is in the Validate.

see the image below when I comment the loop section.

I'm not sure what the business requirements for your validations. but just make sure that part is returning the correct value first.

enter image description here