I have two fields in reactive form in angular. I need to validate second field and show error message based on first field option chosen.
Field one:
<label>ID proof </b><span >*</span></label>
<mat-form-field appearance="outline">
<mat-select name="idproofs" placeholder="IDProof name" required formControlName="idProof" >
<mat-option *ngFor="let idproof of idproofs" [value]="idproof.value">
{{idproof.viewValue}}
</mat-option>
</mat-select>
</mat-form-field>
Field two:
<label>ID proof number </b><span >*</span></label>
<mat-form-field appearance="outline">
<input matInput placeholder="ID Proof Number" required formControlName="idFroofNumber" >
<mat-error *ngIf="PGroup.controls['idProofNumber'].hasError('required')">
ID proof number is required!
</mat-error>
</mat-form-field>
Example in first field if I choose option as Adhaar number then in second field the validation must be only number with 12 digit characters max length.
If I choose PAN number then only validation will be alphanumeric with 10 characters max length. And accordingly if I can give error message validation as well. How I can achieve that
EDIT added ts formcontrol
this.PGroup = this._formBuilder.group({
idProof: ['', Validators.required],
idProofNumber: ['', [Validators.required,Validators.pattern(/^[\w\s] $/)]]
});
CodePudding user response:
Can you provide your ts code? I see you use ngModel combined with formControlName, and I think that it's combined reactive approach with template driven approach. Anyway, here my advice. Use reactive approach in this way, (only suggestion, you could get the idea and implement base on your preference)
// attr declarations
fieldOne = new FormControl('',[]); // initial value and validators inside array
fieldTwo = new FormControl('', []); // initialValue and validators
// ngOnInit
fieldOne.valueChanges.
.pipe(
debounceTime(100), // dalay after every change to update your validations
distinctUntilChanged(), // only update validators when value change
tap(value => {
// depend on your options you can use switch here
if(value === 'Adhaar number'){
this.fieldTwo.setValidators([Validators.required,Validators.maxLength(12)]);
this.fieldTwo.updateValueAndValidity();
} else
if(value === 'PAN number'){
this.fieldTwo.setValidators([Validators.required,Validators.maxLength(10)]);
this.fieldTwo.updateValueAndValidity();
} else {
this.fieldTwo.setValidators([Validators.required]);
this.fieldTwo.updateValueAndValidity();
}
})
)
.subscribe()
Update your html base on your possible errors .
Also you can use formGroup with two field instead of separated formControls, and only change subscription from this.fieldOne.valueChanges to this.form.get('fieldOne').valueChanges
Remember to unsubscribe from this subscription. I suggest https://www.npmjs.com/package/@ngneat/until-destroy
CodePudding user response:
I think you could do something like this,
Lets say you have component with the name AppComponent,
import { Component } from '@angular/core';
import {
FormBuilder,
FormGroup,
FormControl,
ValidationErrors,
AbstractControl,
ValidatorFn
} from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'stackoverflow-examples';
panelOpenState = false;
idproofOptions: string[] = ['Aadhar','PAN'];
idproofsForm: FormGroup;
get idproofsFormControl(): FormControl {
// debugger
return this.idproofsForm.controls['idproofs'] as FormControl;
}
get idproofsInputFormControl(): FormControl {
// debugger
return this.idproofsForm.controls['idproofsInput'] as FormControl;
}
idproofInputAadharValidation(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
// debugger
const pattern = new RegExp("^[0-9]{10}$");
if (pattern.test(control.value)) {
return null;
} else {
return {
aadharError: 'Aadhar number is not correct'
};
}
};
}
idproofInputPANValidation(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
// debugger
const pattern = new RegExp("^[a-zA-Z0-9] $");
if (pattern.test(control.value)) {
return null;
} else {
return {
panError: 'PAN number is not correct'
};
}
};
}
constructor(
private fb: FormBuilder
) {
this.idproofsForm = new FormGroup(
{
idproofs: new FormControl(''),
idproofsInput: new FormControl('')
}
);
}
ngOnInit() {
this.idproofsForm.valueChanges.subscribe(
field => {
// debugger
console.log(this.idproofsForm.controls['idproofsInput']);
if (field['idproofs'] && field['idproofs'] === 'PAN') {
this.idproofsInputFormControl.clearValidators();
this.idproofsInputFormControl.setValidators(this.idproofInputPANValidation());
} else if (field['idproofs'] && field['idproofs'] === 'Aadhar') {
this.idproofsInputFormControl.clearValidators();
this.idproofsInputFormControl.setValidators(this.idproofInputAadharValidation());
}
// debugger
}
);
}
}
Your HTML can look like this,
<label>ID proof<span >*</span></label>
<mat-form-field appearance="outline">
<mat-select [formControl]="idproofsFormControl" name="idproofs" placeholder="IDProof name" required >
<mat-option *ngFor="let idproof of idproofOptions" [value]="idproof">
{{idproof}}
</mat-option>
</mat-select>
</mat-form-field>
<br/>
<!-- {{idproofsInputFormControl|json}} -->
<label>ID proof number<span >*</span></label>
<mat-form-field appearance="outline">
<input matInput placeholder="ID Proof Number" required [formControl]="idproofsInputFormControl" >
<mat-error *ngIf="idproofsInputFormControl.errors">
{{idproofsInputFormControl.errors['aadharError']}}
</mat-error>
</mat-form-field>
look at the error below in screenshot
Note: My suggestion is that you don't use ngModel the reactive approach is far better, you may have to change this code a little to fit your requirement but I am sure it should be helpful