单击按钮时使用 Reactive 表单进行异步验证
Async validation with Reactive forms on button click
我正在尝试验证数据库中是否存在名称。如果是这样,我想将其标记为无效并通知用户这种情况。
除此之外,我正在寻找执行此操作的最佳方法:模板驱动表单或反应式表单。
请注意,我使用了多种形式,如 https://material.angular.io 中的示例。
这是我当前的代码:
HTML:
<mat-horizontal-stepper [@.disabled]="true" #stepper>
<mat-step label="Example" [stepControl]="nameForm">
<form [formGroup]="nameForm">
<mat-form-field>
<mat-label>NAME</mat-label>
<input matInput placeholder="Input name" formControlName="name">
</mat-form-field>
<button
matTooltip="Verify name in data base"
(click)="validateName()" <<<< LOOK, HERE IS THE POINT
[disabled]="!loading">Validate Name
<i class="ml-2 fa fa-refresh fa-spin" *ngIf="!loading"></i>
</button>
<div>
<button mat-button matStepperNext>Next</button>
</div>
</form>
</mat-step>
<mat-step label="Endereço" [stepControl]="enderecoForm">
<form [formGroup]="nameForm2">
<mat-form-field>
...
</mat-form-field>
</form>
</mat-step>
... more steps with one form inside of each one
TS:
export class SomeComponent {
constructor(private readonly _formBuilder: FormBuilder) {}
ngOnInit(): void {
this.nameForm = this._formBuilder.group(
{
name: ['', Validators.required],
},
{ validator: this.validateName },
); // AND HERE LOOK
this.nameForm2 = this._formBuilder.group({
name2: ['', Validators.required],
// ... more forms and controls validators with reactive forms
});
}
validateName(fb: FormGroup) {
const nameCtrl = fb.get('name');
// method verifyName return the name from database if exists
const name = this.nameService.verifyName(nameCtrl);
if (nameCtrl.errors == null || 'exists' in nameCtrl.errors) {
if (name == null) {
nameCtrl.setErrors(null);
} else {
nameCtrl.setErrors({ exists: true });
}
}
}
}
我不太明白你是想使用 AsyncValidator
还是在点击按钮后进行验证,但我们开始吧:
1。通过 asyncValidators
:
验证
import { Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { Observable, of as just } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'app-some',
templateUrl: './some-component.html'
})
export class SomeComponent {
readonly formGroup = this.formBuilder.group(
{
name: ['', Validators.required, this.validateName]
},
{ updateOn: 'blur' }
);
constructor(private readonly formBuilder: FormBuilder) {}
// For this case, I'd suggest you to name it something like `nameAsyncValidator`.
// In any case it's up to you :)
private validateName(): Observable<{ exists: boolean } | null> {
const { errors, value } = this.formGroup.get('name')!;
if (errors || !value) return just(null);
return this.nameService.verifyName(value).pipe(
catchError(() => just(null))
);
}
}
- 请注意,我将
this.validateName
指定为第 3 个。参数,具体哪个是asyncValidators
.
- 另请注意,我使用
{ updateOn: 'blur' }
来防止对服务器进行不必要的请求。这些请求只会在模糊时触发。
- DEMO (full version, including loading handling and mocked version)
2。要通过单击按钮进行验证:
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { catchError, take, tap } from 'rxjs/operators';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'app-some',
templateUrl: './some-component.html'
})
export class SomeComponent {
readonly formGroup = this.formBuilder.group({
name: ['', Validators.required]
});
validateName(): void {
const nameFormControl = this.formGroup.get('name')!;
const { errors, value } = nameFormControl;
if (errors || !value) return;
return this.nameService.verifyName(value).pipe(
take(1),
catchError(() => just(null)),
tap(exists => nameFormControl.setErrors(exists))
)
.subscribe();
}
}
请注意,由于您没有在问题中包含 nameServive.verifyName
的签名,我假设它 returns Observable<{ exists: boolean } | null>
.
我正在尝试验证数据库中是否存在名称。如果是这样,我想将其标记为无效并通知用户这种情况。
除此之外,我正在寻找执行此操作的最佳方法:模板驱动表单或反应式表单。
请注意,我使用了多种形式,如 https://material.angular.io 中的示例。
这是我当前的代码:
HTML:
<mat-horizontal-stepper [@.disabled]="true" #stepper>
<mat-step label="Example" [stepControl]="nameForm">
<form [formGroup]="nameForm">
<mat-form-field>
<mat-label>NAME</mat-label>
<input matInput placeholder="Input name" formControlName="name">
</mat-form-field>
<button
matTooltip="Verify name in data base"
(click)="validateName()" <<<< LOOK, HERE IS THE POINT
[disabled]="!loading">Validate Name
<i class="ml-2 fa fa-refresh fa-spin" *ngIf="!loading"></i>
</button>
<div>
<button mat-button matStepperNext>Next</button>
</div>
</form>
</mat-step>
<mat-step label="Endereço" [stepControl]="enderecoForm">
<form [formGroup]="nameForm2">
<mat-form-field>
...
</mat-form-field>
</form>
</mat-step>
... more steps with one form inside of each one
TS:
export class SomeComponent {
constructor(private readonly _formBuilder: FormBuilder) {}
ngOnInit(): void {
this.nameForm = this._formBuilder.group(
{
name: ['', Validators.required],
},
{ validator: this.validateName },
); // AND HERE LOOK
this.nameForm2 = this._formBuilder.group({
name2: ['', Validators.required],
// ... more forms and controls validators with reactive forms
});
}
validateName(fb: FormGroup) {
const nameCtrl = fb.get('name');
// method verifyName return the name from database if exists
const name = this.nameService.verifyName(nameCtrl);
if (nameCtrl.errors == null || 'exists' in nameCtrl.errors) {
if (name == null) {
nameCtrl.setErrors(null);
} else {
nameCtrl.setErrors({ exists: true });
}
}
}
}
我不太明白你是想使用 AsyncValidator
还是在点击按钮后进行验证,但我们开始吧:
1。通过 asyncValidators
:
验证
import { Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { Observable, of as just } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'app-some',
templateUrl: './some-component.html'
})
export class SomeComponent {
readonly formGroup = this.formBuilder.group(
{
name: ['', Validators.required, this.validateName]
},
{ updateOn: 'blur' }
);
constructor(private readonly formBuilder: FormBuilder) {}
// For this case, I'd suggest you to name it something like `nameAsyncValidator`.
// In any case it's up to you :)
private validateName(): Observable<{ exists: boolean } | null> {
const { errors, value } = this.formGroup.get('name')!;
if (errors || !value) return just(null);
return this.nameService.verifyName(value).pipe(
catchError(() => just(null))
);
}
}
- 请注意,我将
this.validateName
指定为第 3 个。参数,具体哪个是asyncValidators
. - 另请注意,我使用
{ updateOn: 'blur' }
来防止对服务器进行不必要的请求。这些请求只会在模糊时触发。 - DEMO (full version, including loading handling and mocked version)
2。要通过单击按钮进行验证:
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { catchError, take, tap } from 'rxjs/operators';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'app-some',
templateUrl: './some-component.html'
})
export class SomeComponent {
readonly formGroup = this.formBuilder.group({
name: ['', Validators.required]
});
validateName(): void {
const nameFormControl = this.formGroup.get('name')!;
const { errors, value } = nameFormControl;
if (errors || !value) return;
return this.nameService.verifyName(value).pipe(
take(1),
catchError(() => just(null)),
tap(exists => nameFormControl.setErrors(exists))
)
.subscribe();
}
}
请注意,由于您没有在问题中包含 nameServive.verifyName
的签名,我假设它 returns Observable<{ exists: boolean } | null>
.