问题描述
this.form = this.fb.array([
this.fb.group({
username: [null,Validators.required]
}),this.fb.group({
username: [null,...
],uniqueUsernameValidator)
const uniqueUsernameValidator = control => {
// find duplicate
// when flagging the error
control.get('0.username').setErrors(ifThereIsAnError) // where `ifThereIsAnError` Could be null
}
某种程度上,uniqueUsernameValidator
正在使子表单字段指定的required
验证静音。如何解决这个问题?
解决方法
尝试以下方法。 See below Link on Stackblitz
const uniqueUsernameValidator = (control: FormArray) => {
let nonUnique = (control.value.map(({username}) => username) as any[]).filter((v,i,a) =>
a.indexOf(v) !== i)
if(nonUnique.length > 0) {
return {nonUnique}
}
return null
}
这将验证用户提供的用户名是否唯一
,为控件创建自定义验证器时,应实现ValidatorFn interface:
interface ValidatorFn {
(control: AbstractControl): ValidationErrors | null
}
实现可能如下所示:
const uniqueUsernameValidator = control => {
const ifThereIsAnError = /* your logic .. find duplicate */
if(!ifThereIsAnError) {
return null
}
return { unique: true }
}
就您而言,我认为您的意思是setErrors
而不是setError
?使用AbstractControl的错误设置器时,您将使用setErrors
覆盖所有错误。如果您想保留解决方案而不使用ValidatorFn,则应先获取错误,然后再设置新错误:
const uniqueUsernameValidator = control => {
const errors: ValidationErrors = this.control.errors;
const keyNameOfYourError = 'unique';
// reset the last error before revalidating
if (errors && errors[keyNameOfYourError]) {
delete errors[keyNameOfYourError];
}
// ifThereIsAnError = execute your logic ... find duplicate
if (ifThereIsAnError) {
errors[keyNameOfYourError] = true;
this.control.setErrors(errors);
}
}
但是我建议您使用实现ValidatorFn。
,我会这样解决:
https://stackblitz.com/edit/form-array-angular-ax9vna?file=src%2Fapp%2Fapp.component.ts
我的提示和技巧
-
我喜欢验证在那里显示错误的地方。因此,控件本身正在处理其
errors
属性。 -
确保您的控件知道什么时候更改了
this.sub = this.usernamesArr.valueChanges.subscribe(_ => updateControls(this.usernamesArr) );
-
我们需要访问用户名控件中的formArray-value。通过绑定
this
我们将有权使用它。uniqueInArray = uniqueInArray.bind(this);
(从stackblitz复制的代码)
html
<div [formGroup]="usernamesArr"
*ngFor="let item of usernamesArr.controls; let i = index;">
<div [formGroupName]="i">
<input formControlName="username" [placeholder]="'username' + i">
<pre style="color: red">
{{usernamesArr.controls[i].controls.username.errors | json}}
</pre>
</div>
</div>
<button (click)="addItem()">Add</button>
ts
import { Component } from "@angular/core";
import {
FormBuilder,FormGroup,FormArray,FormControl,Validators,AbstractControl
} from "@angular/forms";
import { Subscription } from "rxjs";
const uniqueInArray = function(
control: AbstractControl
): { uniqueInArray: string } | null {
const value = control.value;
const arr = this.usernamesArr.value as { username: string }[];
if (value == null || value.length === 0 || arr == null || arr.length < 2) {
return null;
}
return arr.map(_ => _.username).filter(_ => _ == value).length > 1
? { uniqueInArray: `${value} already exists.` }
: null;
};
const updateControls = function(control: FormArray): null {
control.controls.map((c: FormGroup) =>
c.controls.username.updateValueAndValidity({
onlySelf: true,emitEvent: false
})
);
return null;
};
@Component({
selector: "my-app",templateUrl: "./app.component.html",styleUrls: ["./app.component.css"]
})
export class AppComponent {
sub: Subscription;
usernamesArr: FormArray;
uniqueInArray = uniqueInArray.bind(this);
constructor(private formBuilder: FormBuilder) {}
ngOnInit() {
this.usernamesArr = new FormArray([]);
this.sub = this.usernamesArr.valueChanges.subscribe(_ =>
updateControls(this.usernamesArr)
);
}
createItem(): AbstractControl {
return new FormGroup({
username: new FormControl("",[Validators.required,this.uniqueInArray])
});
}
addItem(): void {
this.usernamesArr.push(this.createItem());
}
ngOnDestroy() {
this.sub.unsubscribe();
}
}
,
回答更普遍的设置错误问题,而不必消除其他验证程序的错误:
// Add - retaining existing
const errors = {
...control.errors,myError: myValue
};
control.setErrors(errors);
// Remove - only the one we care about
const errors = { ...control.errors };
delete errors.myError;
control.setErrors(Object.keys(errors).length ? errors : null);