问题
I am trying to create a custom reactive form validation that allows me to pass an array of data to check if a string already exists. I am able to do it one way, so that it would be a form level validation but I can't get it to work on an individual form control.
Form level validation
This will create the error on the whole form, not just that control
this.myForm = this.fb.group({
name: ['', Validators.compose([
Validators.required,
]),
],
}, {
validator: (formGroup: FormGroup) => this.checkStringExists(
formGroup.controls.name,
this.arrayOfStrings,
),
});
Custom validation that allows me to take in the form control and check it against an array that is passed in.
checkStringExists(formInput: AbstractControl, names: string[]): { [s: string]: boolean } {
if (names && names.length && formInput && formInput.value) {
const isUnique = !names.find((name) => name === formInput);
if (isUnique) {
return { nameExists: true };
}
}
return null;
}
Individual control validation (The way I would like to do this)
This will create the error only on the specific control
this.myForm = this.fb.group({
name: ['', Validators.compose([
Validators.required,
this.checkStringExists(this.arrayOfStrings),
]),
],
});
Custom validation that allows me to take only the array as part of the Validators.compose[] Here
checkStringExists(names: string[]): ValidatorFn {
return (formInput: AbstractControl): ValidationErrors | null => {
if (names && names.length && formInput && formInput.value) {
const isUnique = !names.find((name) => name === formInput);
if (!isUnique) {
return { nameExists: true };
}
}
return null;
};
}
This subscription sets the value for arrayOfStrings.
mySubscription.subscribe((value: string[]) => {
this.arrayOfStrings = value;
})
The problem I have is arrayOfStrings may update multiple times. if I use the validation the first way, the arrayOfStrings is up to date. If I use the validation the second way, arrayOfStrings is null/initial value.
I am trying to get this validation to work the second way, so that I can display validation based on an individual control, not if the whole form has this error. Does anyone know how I can pass this value and keep it up to date?
I also have the validation functions in a separate helper file for reusability across the app.
Here is an example
回答1:
Update
my bad! you can pass an array, an array is an inmutable value. so if you has
export function findArray(array){
return (control=>{
return array.indexOf(control.value)<0?{error:'not match'}:null
})
}
array=['one','two']
control=new FormControl(null,findArray(this.array))
you can see a simple stackblitz
Really a validator can not has "dynamic" argument. so some like
foolValidator(name:string)
{
return (control:AbstractControl)=>{
return control.value!=name?{error:'it's not the name'}:null
}
}
name="joe"
control=new FormControl(null,foolValidator(this.name))
Only take account 'joe', not the name of variable "name"
You can use bind(this)
, bind change the "scope", see e.g. this link
foolValidator() //see that you not pass the argument
{
return (control:AbstractControl)=>{
//see that you use "this.name"
return control.value!=this.name?{error:'it's not the name'}:null
}
}
name="joe"
control=new FormControl(null,foolValidator().bind(this))
来源:https://stackoverflow.com/questions/65813583/angular-forms-using-custom-validation-and-a-dynamic-value