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.
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;