1

I want to create custom component for input text but I don't know how can I bind validation of each input field to custom component. Is there any way to set errors of each field as array of objects like below

<app-custom-input formControlName="username" label="Username" [errors]="[ { type: 'required', message: 'is required' }, { type: 'minlength', message: 'min length error' } ]" ></app-custom-input> 

In fact, I want to write reusable components that have different validations, for example, one field is required and another field has a length condition.

stackblitz.io

1
  • Haven't done it myself, but I think reactive forms is specifically designed for these scenarios. It supports more advanced stuff than the classic html validation Commented Apr 14, 2023 at 9:54

1 Answer 1

1

Step by step.

When we make a custom form control with validate the function validate should return or an object or null -if no errors-.

 public validate(c: FormControl) { const errors: any[] = []; if (!this.control) this.control = c; this.errors && this.errors.forEach((error) => { if (error.type == 'required') { if (!c.value) { errors.push({ required: true, message: error.message }); } } }); return errors.length ? { error: errors } : null; } 

See that return an object with an unique property "error" that is an array of errors.

This error is an error not of the inner control -you input inside the custom-input- else the FormControl declared in "parent". So, to iterate over this error inside the custom form control we need "get it".

Generally you use the constructor

constructor(@Optional() @Self() public ngControl: NgControl){ if (this.ngControl != null) { this.ngControl.valueAccessor = this; } } 

But, as we have a validate function simply we declare, and give value to it in validate function

 control!: AbstractControl; public validate(c: FormControl) { if (!this.control) this.control = c; ...rest of the code.. } 

The last is iterate over this errors

<ng-container *ngIf="control?.touched || control?.dirty"> <ng-container *ngFor="let error of control?.errors?.error"> <div class="error-text"> {{ error.message }} </div> </ng-container> </ng-container> 

Your forked stackblitz

NOTE: See that you needn't add the validators to your FormControls

In your code:

registrationForm = this.fb.group({ username: [''], password: [''], }); 

update to only send if reuired we need use some like

 public validate(c: FormControl) { const errors: any[] = []; if (!this.control) this.control = c; cons errorRequired==!c.value && this.errors?this.errors.find(x=>x=='required'):null if (errorRequired) return [{required:true,message:errorRequired.message}] this.errors && this.errors.forEach((error) => { if (error.type == 'required') { if (!c.value) { errors.push({ required: true, message: error.message }); } } }); return errors.length ? { error: errors } : null; 
Sign up to request clarification or add additional context in comments.

6 Comments

Thank you so much, it works for required validation, but what should I do for use customer validators like min length? please check this link
@user21636825, in the function válidamente, inside the loop of errores, chech if error.type=='minlength' add a new object to the array errors and so. Note: generally if the error is required you should not add new error.
how can I prevent other errors when I get required?
When compare if required, if not fullfilled, instead of push, use errors=[{required: true, message: error.message }];return;
Sorry, you're in a fooreach, so you can not use return. You need, before the foreach, ask about if (errors && errors.find(x=>x.type=='required') I just update que answer
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.