我一直在尝试使用以下自定义验证程序。它检查数据库以查看用户是否输入了唯一的用户名,但始终输入各种错误。我将“ control:AbstractControl”这一行代码移到了包括http服务调用上方的位置,但仍然出错。该服务可以正常工作,并返回用户名对象,例如user_name:“ preston”。
我怀疑问题是我没有关于如何使用表单控件的思维导图,并且在线上的大多数信息都没有涉及到数据库,然后是有条件的。我还怀疑我将表单控件概念与普通的旧表单事件交互方法混合在一起,但失败了。我该如何解决此问题?
模板的一部分:
<form [formGroup]="addEditMemberForm"
(ngSubmit)="onSubmit(addEditMemberForm.value)"
[class.error]="!addEditMemberForm.valid && addEditMemberForm.touched">
<mat-form-field class="inputBackground">
<input matInput #userName maxlength="30" class="inputField"
type="text" id="user_name"formControlName="user_name"
[errorStateMatcher]="matcher" required
(blur)="validateUsername(userName.value)">
<mat-hint align="end">{{userName.value?.length || 0}}/30</mat-hint>
<div class="formError"
*ngIf "this.addEditMemberForm.controls['user_name']
.hasError('alreadyExist')">
This username already exists in our database</div>
<mat-error>{{ errors.required }}</mat-error>
</mat-form-field>
</form>
在组件上:
private createForm() {
this.addEditMemberForm = this.fb.group({
...
user_name: ['', Validators.required, this.validateUsername()],
...
}
private validateUsername(userName) {
this.httpService.validateUsername(userName)
.subscribe(res => {
const convertedName = res['user_name']; // Convert from object to string.
if (convertedName === userName): ValidatorFn {
console.log('res if: ', res);
// return (control: AbstractControl): { [key: string]: boolean } => {
return (control: AbstractControl): ValidationErrors | null => {
return {'alreadyExist': true};
};
}
return null; // This result means that the user name isn't taken and do nothing.
},
(err: HttpErrorResponse) => {
console.log(err.error);
console.log(err.message);
}
);
}
答案 0 :(得分:2)
尝试
user_name: ['', Validators.required, this.validateUsername().bind(this)],\
和
private validateUsername(control: AbstractControl) {
const val = control.value;
return this.httpService.validateUsername(val).pipe(
map(res => {
const convertedName = res['user_name'];
return convertedName === val ? { alreadyExist: true } : null;
}),
catchError(err => {
console.log(err.error);
console.log(err.message);
this.messagesService.openDialog('Error aef-2', 'Database not available.');
return Observable.of({ "error": true })
})
);
显然,这可能不起作用,因为我不知道您的服务如何工作,也不知道我将提供什么样的响应。但是服务是在用户输入时调用的,这很好。您只需向管道添加catchError
即可处理异常,就可以了。
答案 1 :(得分:0)
@Antoniossss对我的原始问题有一个可行的答案,他对指导我了解其工作原理非常有帮助。但是,如果您使用.bind(this),则会有一个服务器调用,其中包含用户键入的每个字母,例如增量搜索。如果您检查服务器和控制台日志,这可能很难看。
在发布本文时,Angular表单控件updateOn:'blur'有一个pull请求,将其添加到FormGroup的控件中。截至2018年6月26日,它不起作用。这可能使@Antoniossss的解决方案成为更好的选择。
我重构了他的代码,经过多次实验找到了解决方案。一个更优雅的解决方案会很好。
html。注意简单的* ngIf。如果绑定的var为true,则显示消息。
<li>
<label class="label-pad">User name: </label>
<mat-form-field class="inputBackground">
<input matInput #userName maxlength="30"
class="inputField" type="text" id="user_name"
formControlName="user_name"
[errorStateMatcher]="matcher" required
(blur)="validateUsername(userName.value)">
<mat-hint align="end">{{userName.value?.length || 0}}/30</mat-hint>
<div class="formError" *ngIf = "this.inDatabase === true">
This username already exists in our database</div>
<mat-error>{{ errors.required }}</mat-error>
</mat-form-field>
</li>
组件。创建表单时,没有自定义验证器,也没有绑定。绑定可以消除html中的模糊。它必须走了。当用户离开该字段并传递该字段的值时,Blur将调用一个函数。调用的函数从db获取正确的对象,仅调用一次,然后提取用户名字符串。然后,它测试条件,如果存在匹配项,则调用另一个函数来处理控件,并使用setTimeout显示和清除消息,并清除将其设置为无效的字段。然后用户再次尝试。当然,这应该在服务中,但在这里我将使其保持简单。 Stackbliz代码经常更改,因此有一天可能会投入使用。
...
public inDatabase = false; // Form validation - userName taken...
...
constructor(
private fb: FormBuilder,
private httpService: HttpService,
private messagesService: MessagesService,
) { }
ngOnInit() {
this.createForm();
}
// The reactive model that is bound to the form.
private createForm() {
this.addEditMemberForm = this.fb.group({
id: [''],
first_name: ['', Validators.required],
last_name: ['', Validators.required],
user_name: ['', Validators.required],
country: ['', Validators.required],
});
}
private validateUsername(userName) {
return this.httpService.validateUsername(userName)
.subscribe(res => {
const extractedName = res.map(x => x.user_name); // array
// Convert from array to string.
const convertedName = extractedName.toString();
// If the condition is met then call the isTaken function below.
return convertedName === userName ? this.isTaken() : null;
},
(err: HttpErrorResponse) => {
console.log(err.error);
console.log(err.message);
this.messagesService.openDialog('Error', 'Delete did not happen.');
}
);
}
public isTaken() {
this.inDatabase = true; // Var is bound to html conditional.
// Remove the already in database message after some time.
setTimeout (() => {
this.inDatabase = false;
}, 2000);
// Clear the field to reset validation and prepare for next attempt.
this.addEditMemberForm.controls['user_name']
.setValue(null);
}