问题
I am trying to figure out the best way to implement cross field validation in Angular.
For example, I have a select field that makes another field mandatory.
I want to be able to:
- Change the border color of the field if it is invalid
- Display a * in front of the field whenever it becomes mandatory
- Display a specific error message that explains what validation rule is broken.
So far, I came up with three solutions but they don't feel so convincing to me.
- Listen to select field changes and update second field's validators.
- Listen to both fields changes and manually perform setErrors
- Lift validation to formGroup (which can feel extremely cumbersome since, validation state is now stored in formGroup and not directly available in formControl).
Here is a Stackblitz implementation that demos my investigations.
回答1:
For cross field validation, you can use required validation of @rxweb/reactive-form-validation.
You just have to mention conditionalExpression in your formControl like this:
input2:['', RxwebValidators.required({conditionalExpression:'x => x.input1 == "mandatory"' })]
and set the error message in your app.component.ts like this
ngOnInit(){
ReactiveFormConfig.set({"validationMessage":{"required":"This field is required"}});
}
Here is your complete component code:
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder } from "@angular/forms"
import { RxwebValidators } from '@rxweb/reactive-form-validators';
@Component({
selector: 'app-required-conditionalExpression-validator',
templateUrl: './required-conditional-expression.component.html'
})
export class RequiredConditionalExpressionValidatorComponent implements OnInit {
userFormGroup: FormGroup
constructor(
private formBuilder: FormBuilder )
{ }
ngOnInit() {
this.userFormGroup = this.formBuilder.group({
input1:[''],
input2:['', RxwebValidators.required({conditionalExpression:'x => x.input1 == "mandatory"' })],
});
}
}
Here is your Complete HTML Code:
<div>
<form [formGroup]="userFormGroup">
<div>
<label>Mandatory/Optional </label>
<select formControlName="input1">
<option value="optional">Optional</option>
<option value="mandatory">Mandatory</option>
</select>
<br/><br/>
</div>
<div>
<label>Input2</label>
<input type="text" formControlName="input2"/><br/>
<span>
{{userFormGroup.controls.input2.errors?.required?.message}}
</span>
</div>
</form>
</div>
Here is the Working Example
回答2:
the best way is the simpler way. Just use a customValidator like
form:FormGroup=new FormGroup({
input1: new FormControl('optional'),
input2: new FormControl(null,this.customValidator())
})
customValidator()
{
return ((control:any)=>{
if (!control.parent)
return null;
let mandatory=control.parent.get('input1').value;
return mandatory!='optional' && !control.value?{required:true}:null
})
}
Another option for not ask for control.parent it's use bind(this). This allow us have inside the validator to all the variables of our component, and of course access to this.form
form:FormGroup=new FormGroup({
input1: new FormControl('optional'),
input2: new FormControl(null,this.customValidator().bind(this)) //<--bind(this)
})
customValidatorBind()
{
return ((control:any)=>{
if (!this.form)
return null;
let mandatory=this.form.get('input1').value;
return mandatory!='optional' && !control.value?{required:true}:null
})
}
Well, as we want that when change 'input1' 'input2' was checked, you need use, after create the form subscribe to valueChanges
this.form.get('input1').valueChanges.subscribe(()=>{
this.form.get('input2').updateValueAndValidity()
})
UPDATED there's another aproach. Create the customValidator over the form and use the validator to use setError to the control required. Using setError, make that Angular add ng-invalid for us, ad we NEEDN't subscribe to value change. See
form:FormGroup=new FormGroup({
input1: new FormControl('optional'),
input2: new FormControl(null)
},{validators:this.customValidatorForm()})
customValidatorForm()
{
return (form:FormGroup)=>{
const error=form.get('input1').value!='optional' &&
!form.get('input2').value?{required:true}:null;
form.get('input2').setErrors(error); //<--see the setErrors
return error;
}
}
See stackblitz
来源:https://stackoverflow.com/questions/57120135/best-way-to-implement-angular-cross-field-validation