获取 FormGroup/FormControl 中存在的验证器
Posted
技术标签:
【中文标题】获取 FormGroup/FormControl 中存在的验证器【英文标题】:Get validators present in FormGroup/FormControl 【发布时间】:2017-10-05 21:25:59 【问题描述】:我在我的应用程序中使用 Material 2,但在这个问题中,我想专门解决 Input 的问题。
正如您在 API 参考 中看到的,有一个名为 required
的属性绑定,它在占位符中显示为星号。
所以,我想知道是否有办法检查表单控件在 Angular 中是否有特定的验证器,因为我真的不想为每个输入手动设置[required]="true/false"
我阅读了AbstractControl 文档,但没有找到任何相关信息。我遇到了hasError
方法(讽刺的是没有记录在 nowhere ......无论是在 FormGroup 还是在 FormControl 或AbstractControl),但这不是我想要的。它只是检查表单控件是否有错误,但正如您可能已经阅读的那样,我想检查该控件是否有一些特定的验证器...
一些代码:
<md-input-container>
<input placeholder="Placeholder"
mdInput [formControl]="anyCtrl"
[required]="anyCtrl.hasValidator('required')"> <!-- something like this -->
</md-input-container>
我希望问题足够清楚。提前致谢。
【问题讨论】:
是的,我检查了错误并意识到其他一些问题,所以你最好等待比我聪明的人:P 无论如何,谢谢你的尝试:) @AJT_82 我刚刚开始赏金...如果您有解决方案甚至解决方法,请发布。 是的,我一定会在有时间的时候尝试一下。这个问题开始困扰我:P @AJT_82 这是糟糕的解决方法:) plnkr.co/edit/qUtwnwLYh1YSH4zr8ZK4?p=preview 【参考方案1】:Angular 并没有真正提供一个很好的、干净的方法来做到这一点,但它是可能的。我认为验证器存储在注入到 FormBuilder(NG_VALIDATORS
) 中的服务中,我将研究劫持该服务或将其注入到组件中,但现在这将起作用:
docs 和源在AbstractControl
上显示validator
成员键入ValidatorFn
。 ValidatorFn
不幸的是只有 null
打字,所以我们看不到发生了什么。但是,在查看生成的源并探测应用程序之后,似乎我们可以将这个validators
方法传递给control
参数,该参数将返回该控件上存在的所有验证器的对象,无论它是否通过。
奇怪的是,这个仅适用于 FormControl
本身而不是 FormGroup
(在 FormGroup
上,validators
成员不是函数,并且始终是 null
我的测试)。编译后的 JS 说这个函数有一个control
参数;我试过传入FormControl
引用,但据我所知,只要此参数不为空,它只会返回控件上的验证器。
在 FormControl 上获取验证器
// in the constructor
this.myForm = this.formBuilder.group(
'anyCtrl': ['', Validators.required],
'anotherCtrl': ['', Validators.compose([Validators.required, Validators.email])]
);
// later on
let theValidators = this.myForm.controls['anyCtrl'].validator('');
console.log(theValidators) // -> required: true;
let otherValidators = this.myForm.controls['anotherCtrl'].validator('');
console.log(otherValidators); // -> required: true, email: true
更容易抓取:
public hasValidator(control: string, validator: string): boolean
return !!this.myForm.controls[control].validator(control).hasOwnProperty(validator);
// returns true if control has the validator
在您的标记中:
<md-input-container>
<input placeholder="Placeholder"
mdInput [formControl]="anyCtrl"
[required]="hasValidator('anyCtrl', 'email')">
</md-input-container>
Validators.required 的特殊情况
required
验证器有一个快捷方式。 [required]
绑定实际上是 RequiredValidator
directive 的一个实例(source/forms.js 的第 5022 行)。该指令实际上会将required
Validator 添加到它所在的FormControl
。相当于在初始化时将Validators.required
添加到FormGroup
。因此,将绑定属性设置为 false 将从该控件中删除 required
Validator,反之亦然...无论哪种方式,该指令都会影响 FormControl.required
值,因此将其绑定到它更改的属性不会真正起作用很多。
唯一的区别是[required]
指令将星号添加到占位符,而Validators.required
没有。
我将继续研究 NG_VALIDATORS
,但我希望这对现在有所帮助!
【讨论】:
您好,@joh04667,首先,我只想说:谢谢。我刚刚测试了您的解决方案here,它似乎有效。但是,值得一提的是,如果您有一个没有验证器的control
,它会引发错误,换句话说,validators
是 null。测试它here。话虽如此,多谢一次。
啊,关于你的最后一句话:我知道这就是 差异 只是 星号,这就是我想要它的原因 :) 我希望有一个一天,Material 2 团队实现了一个解决方案来添加星号来寻找响应式表单,而无需在 html 本身中设置 required
。 :)
AFAIK hasValidator()
方法仅适用于“失败”的验证器(即,控件上的必需验证器,值为“”(emtpy))。因为如果他们通过了,他们会返回 null。解决此问题的一种方法是设置该值,使其始终失败并在之后恢复。我将发布一个答案作为后续,因为很难将代码放入 cmets。
其实没有AbstractControls.validators()
方法。如果您的意思是AbstractControls.validators()
,我的最后一条评论代表我的回答也会做出这个假设。
这可行,但似乎只表明验证器失败。例如,当控件具有预设值并且是必需的时。它不会这么说使用这个。我使用了一种解决方法来存储值,将其设置为 null,执行此检查并恢复值。【参考方案2】:
这个答案是@joh04667's 的延续。他们写道:
public hasValidator(control: string, validator: string): boolean
return !!this.myForm.controls[control].validators(control).hasOwnProperty(validator);
// returns true if control has the validator
但是没有AbstractControls.validators()
方法。我假设AbstractControls.validator()
是故意的。
hasValidator()
方法仅适用于“失败”的验证器(例如,控件上的必需验证器,值为 ''(空))。因为如果他们通过了,他们会返回 null。解决此问题的一种方法是设置该值,使其始终失败并在之后恢复。
public hasValidator(control: string, validator: string): boolean
let control: AbstractControl = this.myForm.controls[control];
let lastValue: any = control.value;
switch(validator)
case 'required':
control.setValue(''); // as is appropriate for the control
case 'pattern':
control.setValue('3'); // given you have knowledge of what the pattern is - say its '\d\d\d'
....
let hasValidator: boolean = !!control.validator(control).hasOwnProperty(validator);
control.setValue(lastValue);
return hasValidator;
这太可怕了。它引出了一个问题 - 为什么没有AbstractControl.getValidators(): ValidatorFn[]|null
?
隐藏这个的动机是什么?也许他们担心有人可能会输入他们的代码:
...
secretPassword: ['', [Validators.pattern('fjdfjafj734738&UERUEIOJDFDJj')]
...
【讨论】:
“为什么没有 AbstractControl.getValidators(): ValidatorFn[]|null?” 我只是关注这个issue(没有任何观点)。跨度> 是的,几个月前我评论过那张票。还没有任何相关人士的消息。 :(!!
在您的hasValidator()
方法中是什么意思?谢谢。
@moreirapontocom ,它被称为双重否定。就我个人而言,我不喜欢它,但它是一种说法,“如果不是空的,那就是真的”首先使其为真或假,然后将其反转,所以“非(非空)”就是你得到的。见***.com/questions/10467475/…【参考方案3】:
没有直接或干净的方式来做到这一点。这是我遇到的工作最干净的方法。使用最新版本的 Angular v10.2.0 测试(截至今日)
导入这些
import AbstractControl, FormControl, Validators from '@angular/forms';
定义你的控制
anyCtrl = new FormControl('', [Validators.required]);
添加此方法
public hasRequiredField = (abstractControl: AbstractControl): boolean =>
if (abstractControl.validator)
const validator = abstractControl.validator(as AbstractControl);
if (validator && validator.required)
return true;
return false;
如何从 HTML 调用这个方法
<input placeholder="Placeholder" [formControl]="anyCtrl" [required]="hasRequiredField(anyCtrl)">
从构造函数或 ngOnInit 中的 Typescript 文件(逻辑)调用它
constructor()
console.log(this.hasRequiredField(this.anyCtrl)); // true, false if Validators array does not contain Validators.required
【讨论】:
【参考方案4】:Angular v12.2 引入hasValidator()
:https://github.com/angular/angular/pull/42838
示例:this.formGroup.controls['anyCtrl'].hasValidator(Validators.required)
您也可以稍后以编程方式添加或删除验证器。请参阅addValidators()
、removeValidators()
等。
https://angular.io/api/forms/AbstractControl#hasValidator
【讨论】:
这是正确的答案。 AbstractControl API 正式支持这一点。 "提供的验证器必须是对所提供的完全相同函数的引用。"这也使它变得毫无用处。如果我设置 Validators.min(0),检查 control.hasValidator(Validators.min(0)) 会返回 false,只有验证相同的确切验证实例才会返回 true。【参考方案5】:根据 mtinner 的推荐 https://github.com/angular/angular/issues/13461#issuecomment-340368046,我们构建了自己的指令来相应地标记必填字段。
@Directive(
selector: '[mandatoryField]'
)
export class MandatoryFieldDirective implements OnInit
hasRequiredField(abstractControl: AbstractControl)
if (abstractControl.validator)
const validator = abstractControl.validator( as AbstractControl);
if (validator && validator.required)
return true;
return false;
ngOnInit()
const required = this.hasRequiredField(this.ngControl.control);
if (required)
this.renderer.setAttribute(this.elementRef.nativeElement, 'required', '');
if (this.parentFormField && this.parentFormField._elementRef) // required for Angular Material form-fields
this.renderer.setAttribute(this.parentFormField._elementRef.nativeElement, 'required', '');
constructor(
private ngControl: NgControl, @Optional() private parentFormField: MatFormField,
public renderer: Renderer2, public elementRef: ElementRef
)
该指令设置了一个“必需”属性。这个属性可以通过 CSS 来解决。该指令适用于普通的 HTML 输入标签以及 Angular Material 表单字段。要使用 Angular Material,我们必须添加一些解决方法,因为必须在封闭的表单字段标签上设置“必需”属性;不仅在实际输入字段上。因此父组件传递给指令构造函数。
<mat-form-field class="date-picker-form">
<input matInput class="label-value" [formControlName]="controlName" mandatoryField [matDatepicker]="picker">
<mat-datepicker #picker class="calendar"></mat-datepicker>
</mat-form-field>
【讨论】:
【参考方案6】:我将 joh04667 和 HankCa 的代码调整为:
export const hasValidator = (form: FormGroup, controlPath: string, validator: string): boolean =>
const control = form.get(controlPath);
const validators = control.validator(control);
return !!(validators && validators.hasOwnProperty(validator));
;
我将其存储在一个名为 util.ts 的文件中并导入包含如下表单的组件中:
import * as util from '@app/shared/util';
并在你的类中定义 util:
public util = util;
像这样将指令添加到您的输入组件中:
[required]="util.hasValidator(myForm, 'path.to.control', 'required')"
【讨论】:
【参考方案7】:请务必记住,使用 setValidator 方法将覆盖您现有的验证器,因此您需要为要重置的控件包含所有需要/想要的验证器。
control.setValidators([myCustomValidator(owner)]);
没有这样干净的方法可以做到这一点,但你总是可以让验证器出现在任何表单组或表单控件中
this.form.controls[key].validator
然后添加您的自定义控件,我们可以这样做
control.setValidators([control.validator, myCustomValidator(owner)]);
这将使我们能够重用现有的验证器以及新的自定义验证器
【讨论】:
【参考方案8】:以Angular v12.2
开头的AbstractControl
应该有a new method:
hasValidator(validator: ValidatorFn): boolean;
【讨论】:
【参考方案9】:使用最新的 Angular 版本 v12.2 和 hasValidator 你可以像这样创建管道:
@Pipe(
name: 'hasRequiredValidator',
)
export class HasRequiredValidatorPipe implements PipeTransform
transform(value: AbstractControl | null, controlName?: string): boolean
const control = controlName ? value?.get(controlName) : value;
return !!control?.hasValidator(Validators.required);
在html模板中
<input [required]="form | hasRequiredValidator: 'controlName'">
或者
<input [required]="form.get('controlName') | hasRequiredValidator">
【讨论】:
以上是关于获取 FormGroup/FormControl 中存在的验证器的主要内容,如果未能解决你的问题,请参考以下文章
如何为combineLatest rxjs Angular编写Jasmine Unit测试用例