如何在表单上实现自定义验证器,其中验证逻辑需要来自可观察对象的数据
How to implement custom validator on a form, where the validation logic requires data from an observable
我正在尝试在表单上实现自定义验证器,它将检查表单输入并将其与值数组进行比较,以确保输入值是唯一的。用于比较的数组来自 Observable。但是,当我尝试从验证方法访问它时,该数组显示为未定义,我假设是因为我以错误的顺序进行订阅,但是我无法弄清楚如何正确执行此操作。
下面是精简的代码,应该显示我正在尝试做的事情。我已经用这两个验证器函数试过了。我还尝试将表单组定义移动到 ngOnInit 中,并在填充 allUserNames 之后直接移动到实际的订阅函数中。
export class userComponent implements OnInit {
user: user = new user;
allUserNames: string[];
generalForm = new FormGroup({
userName: new FormControl(
this.user.name, [
Validators.required,
Validators.maxLength(20),
this.UniqueNameValidator
//,this.UniqueNameValidator1(this.alluserNames)
])
})
constructor(private userService: userService) { }
ngOnInit() {
this.subscribeUsers();
}
subscribeUsers(): void {
this.getUsers().subscribe((users) => {
this.allUserNames = Object.keys(users).map(itm => users[itm].name);
})
}
getUsers(): Observable<user[]> {
return this.userService.getUsers();
}
UniqueNameValidator(allUsers: String[]): ValidatorFn {
return (control: AbstractControl): { [key: string]: boolean } | null => {
allUsers.forEach((userName) => {
if (userName === control.value) {
return { 'notUnique': true };
}
});
return null;
}
}
UniqueNameValidator1(control: AbstractControl): { [key: string]: boolean } | null {
this.allUserNames.forEach((userName) => {
if (userName === control.value) {
return { 'notUnique': true };
}
});
return null;
}
}
我希望验证函数比较输入字符串,并且 return 如果它从 allUserNames 获得匹配则不是唯一的。但是我不断收到以下错误:
ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'forEach' of undefined
如果有人能指出正确的方向,我将不胜感激!
谢谢。
您的 this
范围有问题。如果您要控制台日志 this
,您会发现它没有指向组件,而是指向您的函数。
当您进行 http 调用时,这将是异步验证器的合适位置。另外我建议在构建表单时使用 FormBuilder
,所以...
generalForm: FormGroup;
constructor(private userService: userService, private fb: FormBuilder) {
this.generalForm = this.fb.group({
userName: [this.user.name,
[Validators.required, Validators.maxLength(20)],
[this.UniqueNameValidator.bind(this)]]
})
}
异步验证器作为第三个参数。看到我们还使用 bind(this)
来获得 this
.
的正确范围
然后验证器看起来像这样:
import { of } from 'rxjs';
import { switchMap } from 'rxjs/operators'
UniqueNameValidator(ctrl: AbstractControl) {
return this.userService.getUsers().pipe(
switchMap((users: user[]) => {
// do stuff, and either return error or "of(null)"
})
);
我正在尝试在表单上实现自定义验证器,它将检查表单输入并将其与值数组进行比较,以确保输入值是唯一的。用于比较的数组来自 Observable。但是,当我尝试从验证方法访问它时,该数组显示为未定义,我假设是因为我以错误的顺序进行订阅,但是我无法弄清楚如何正确执行此操作。
下面是精简的代码,应该显示我正在尝试做的事情。我已经用这两个验证器函数试过了。我还尝试将表单组定义移动到 ngOnInit 中,并在填充 allUserNames 之后直接移动到实际的订阅函数中。
export class userComponent implements OnInit {
user: user = new user;
allUserNames: string[];
generalForm = new FormGroup({
userName: new FormControl(
this.user.name, [
Validators.required,
Validators.maxLength(20),
this.UniqueNameValidator
//,this.UniqueNameValidator1(this.alluserNames)
])
})
constructor(private userService: userService) { }
ngOnInit() {
this.subscribeUsers();
}
subscribeUsers(): void {
this.getUsers().subscribe((users) => {
this.allUserNames = Object.keys(users).map(itm => users[itm].name);
})
}
getUsers(): Observable<user[]> {
return this.userService.getUsers();
}
UniqueNameValidator(allUsers: String[]): ValidatorFn {
return (control: AbstractControl): { [key: string]: boolean } | null => {
allUsers.forEach((userName) => {
if (userName === control.value) {
return { 'notUnique': true };
}
});
return null;
}
}
UniqueNameValidator1(control: AbstractControl): { [key: string]: boolean } | null {
this.allUserNames.forEach((userName) => {
if (userName === control.value) {
return { 'notUnique': true };
}
});
return null;
}
}
我希望验证函数比较输入字符串,并且 return 如果它从 allUserNames 获得匹配则不是唯一的。但是我不断收到以下错误:
ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'forEach' of undefined
如果有人能指出正确的方向,我将不胜感激!
谢谢。
您的 this
范围有问题。如果您要控制台日志 this
,您会发现它没有指向组件,而是指向您的函数。
当您进行 http 调用时,这将是异步验证器的合适位置。另外我建议在构建表单时使用 FormBuilder
,所以...
generalForm: FormGroup;
constructor(private userService: userService, private fb: FormBuilder) {
this.generalForm = this.fb.group({
userName: [this.user.name,
[Validators.required, Validators.maxLength(20)],
[this.UniqueNameValidator.bind(this)]]
})
}
异步验证器作为第三个参数。看到我们还使用 bind(this)
来获得 this
.
然后验证器看起来像这样:
import { of } from 'rxjs';
import { switchMap } from 'rxjs/operators'
UniqueNameValidator(ctrl: AbstractControl) {
return this.userService.getUsers().pipe(
switchMap((users: user[]) => {
// do stuff, and either return error or "of(null)"
})
);