我为自定义表单验证器创建了一个可注入服务:
import { Injectable } from '@angular/core'
import { FormControl } from '@angular/forms'
import { Observable, of, timer } from 'rxjs'
import { map, switchMap } from 'rxjs/operators'
@Injectable({
providedIn: 'root'
})
export class IsValidNicknameService {
validate = (time: number = 500) => {
return (input: FormControl) => {
return timer(time).pipe(
switchMap(() => this.isValidNickname(input.value)),
map(isValid => {
return isValid ? null : { shouldNotStartWithA: true }
})
)
}
}
private isValidNickname(value: string): Observable<boolean> {
return this.checkIfFirstLetterIsA(value)
}
private checkIfFirstLetterIsA(value: string): Observable<boolean> {
const firstCharacter = value.toLowerCase().charAt(0)
if (firstCharacter === 'a' || firstCharacter === 'à' || firstCharacter === 'ä' || firstCharacter === 'á' || firstCharacter === 'ã') {
return of(false)
} else {
return of(true)
}
}
}
然后我在我的控制器中这样称呼它:
import { IsValidNicknameService } from './src/Core/Services/isvalidnickname.service'
createNicknamesGroup(): any {
return new FormGroup({
buildingRoom: new FormControl(this.mockBuildingRooms[0], Validators.required),
nickname: new FormControl('', Validators.required, this.isValidNicknameService.validate())
})
}
这是有效的,但我觉得这不是一个好方法。有没有更好、更简洁的方法来实现这一点?
这里没有正确或错误的方法-做任何你觉得舒服的事情。
从您的代码来看,您没有任何服务依赖关系,因此您的服务可以很容易地转换为一组函数。使用普通函数的一个优点是,单元测试可以很容易地覆盖它们。
一般来说,如果需要通过Angular的DI在构造函数中注入一些依赖项,则需要使用@Injectable()
类。但也可以通过将必要的依赖项传递给验证器的高阶函数来避免这种情况:
# my-custom.validator.ts
export const setupRequiredValidator(minLength: number, someCheckerService: CheckerService): ValidatorFn => {
return (control: AbstractControl) => {
return control.value.length >= minLength && someCheckerService.check(control);
}
}
# my.component.ts
constructor(private someCheckerService: CheckerService) {
}
createNicknamesGroup(): FormGroup {
return new FormGroup({
buildingRoom: new FormControl(this.mockBuildingRooms[0], Validators.required),
nickname: new FormControl('', [Validators.required, setupRequiredValidator(5, this.someCheckerService)])
}) // an example of synchronous validator
}
此外,您可以创建@Injectable()
验证器,不要称其为Service
——没有人会对您破口大骂。
# my-custom.validator.ts
@Injectable({
providedIn: 'root'
})
export class MyCustomValidator {
constructor(private router: Router) {}
validateName = (control: AbstractControl) => {
// try to do only one validation per method if there are no dependencies between the validators.
// It's easier to combine and tests them afterwards
return control.value ? null : {error: 'text'};
}
validateEmail = (control: AbstractControl) => {
return control.value ? Validators.email(control) : null;
}
validateSurnameIfTheNameExists = (control: AbstractControl) => {
return control.parent.get('name').valid ? null : this.router.navigate(['/error']);
}
}