从 Angular 2 FormGroup 获取所有验证错误
Posted
技术标签:
【中文标题】从 Angular 2 FormGroup 获取所有验证错误【英文标题】:Get all validation errors from Angular 2 FormGroup 【发布时间】:2017-04-02 11:54:05 【问题描述】:鉴于此代码:
this.form = this.formBuilder.group(
email: ['', [Validators.required, EmailValidator.isValid]],
hasAcceptedTerms: [false, Validators.pattern('true')]
);
如何从this.form
获取所有验证错误?
我正在编写单元测试并希望在断言消息中包含实际的验证错误。
【问题讨论】:
您可以/应该使用 Validators.requiredTrue 代替 Validators.pattern('true') 来强制检查复选框。 【参考方案1】:您可以迭代 this.form.errors 属性。
【讨论】:
我猜this.form.errors
只返回this.form
的验证错误,而不是this.form.controls
。您可以分别验证 FormGroups 及其子项(任意数量的 FormGroups、FormControls 和 FormArrays)。要获取所有错误,我认为您需要递归地询问它们。【参考方案2】:
我遇到了同样的问题,为了找到所有验证错误并显示它们,我写了这个方法:
getFormValidationErrors()
Object.keys(this.productForm.controls).forEach(key =>
const controlErrors: ValidationErrors = this.productForm.get(key).errors;
if (controlErrors != null)
Object.keys(controlErrors).forEach(keyError =>
console.log('Key control: ' + key + ', keyError: ' + keyError + ', err value: ', controlErrors[keyError]);
);
);
表单名称productForm
应更改为您的表单实例名称。
它以这种方式工作:我们从格式为[p: string]: AbstractControl
的表单中获取所有控件,并按每个错误键进行迭代,以获取错误的详细信息。它会跳过 null
错误值。
也可以更改为在模板视图上显示验证错误,只需将console.log(..)
替换为您需要的即可。
【讨论】:
如何将FormArray的上述方法扩展成相同的模式? 您的意思是' + controlErrors[keyErrors];
而不是', controlErrors[keyErrors];
?
我可以从哪里导入 Angular 2 中的 ValidationErrors
?
import ValidationErrors from '@angular/forms';
请注意这不适用于嵌套的 FormGroups,我建议使用 Mayur Dongre 的方法【参考方案3】:
export class GenericValidator
constructor(private validationMessages: [key: string]: [key: string]: string )
processMessages(container: FormGroup): [key: string]: string
const messages = ;
for (const controlKey in container.controls)
if (container.controls.hasOwnProperty(controlKey))
const c = container.controls[controlKey];
if (c instanceof FormGroup)
const childMessages = this.processMessages(c);
// handling formGroup errors messages
const formGroupErrors = ;
if (this.validationMessages[controlKey])
formGroupErrors[controlKey] = '';
if (c.errors)
Object.keys(c.errors).map((messageKey) =>
if (this.validationMessages[controlKey][messageKey])
formGroupErrors[controlKey] += this.validationMessages[controlKey][messageKey] + ' ';
)
Object.assign(messages, childMessages, formGroupErrors);
else
// handling control fields errors messages
if (this.validationMessages[controlKey])
messages[controlKey] = '';
if ((c.dirty || c.touched) && c.errors)
Object.keys(c.errors).map((messageKey) =>
if (this.validationMessages[controlKey][messageKey])
messages[controlKey] += this.validationMessages[controlKey][messageKey] + ' ';
)
return messages;
我从Deborahk拿来并稍微修改了一下。
【讨论】:
【参考方案4】:这是带有FormGroup
内部支持的解决方案 (like here)
测试于:Angular 4.3.6
get-form-validation-errors.ts
import AbstractControl, FormGroup, ValidationErrors from '@angular/forms';
export interface AllValidationErrors
control_name: string;
error_name: string;
error_value: any;
export interface FormGroupControls
[key: string]: AbstractControl;
export function getFormValidationErrors(controls: FormGroupControls): AllValidationErrors[]
let errors: AllValidationErrors[] = [];
Object.keys(controls).forEach(key =>
const control = controls[ key ];
if (control instanceof FormGroup)
errors = errors.concat(getFormValidationErrors(control.controls));
const controlErrors: ValidationErrors = controls[ key ].errors;
if (controlErrors !== null)
Object.keys(controlErrors).forEach(keyError =>
errors.push(
control_name: key,
error_name: keyError,
error_value: controlErrors[ keyError ]
);
);
);
return errors;
使用示例:
if (!this.formValid())
const error: AllValidationErrors = getFormValidationErrors(this.regForm.controls).shift();
if (error)
let text;
switch (error.error_name)
case 'required': text = `$error.control_name is required!`; break;
case 'pattern': text = `$error.control_name has wrong pattern!`; break;
case 'email': text = `$error.control_name has wrong email format!`; break;
case 'minlength': text = `$error.control_name has wrong length! Required length: $error.error_value.requiredLength`; break;
case 'areEqual': text = `$error.control_name must be equal!`; break;
default: text = `$error.control_name: $error.error_name: $error.error_value`;
this.error = text;
return;
【讨论】:
Angular 5 更改 - const controlErrors: ValidationErrors = form.controls[key].errors; 建议检查controlErrors
的真实性,即if (controlErrors)
,因为如果错误为undefined
,则仅检查null
会出错【参考方案5】:
或者您可以只使用这个库来获取所有错误,即使是来自深层和动态表单。
npm i @naologic/forms
如果你想在自己的表单上使用静态函数
import NaoFormStatic from '@naologic/forms';
...
const errorsFlat = NaoFormStatic.getAllErrorsFlat(fg);
console.log(errorsFlat);
如果你想使用NaoFromGroup
你可以导入并使用它
import NaoFormGroup, NaoFormControl, NaoValidators from '@naologic/forms';
...
this.naoFormGroup = new NaoFormGroup(
firstName: new NaoFormControl('John'),
lastName: new NaoFormControl('Doe'),
ssn: new NaoFormControl('000 00 0000', NaoValidators.isSSN()),
);
const getFormErrors = this.naoFormGroup.getAllErrors();
console.log(getFormErrors);
// --> first: ok: false, isSSN: false, actualValue: "000 00 0000"
阅读full documentation
【讨论】:
【参考方案6】:// IF not populated correctly - you could get aggregated FormGroup errors object
let getErrors = (formGroup: FormGroup, errors: any = )
Object.keys(formGroup.controls).forEach(field =>
const control = formGroup.get(field);
if (control instanceof FormControl)
errors[field] = control.errors;
else if (control instanceof FormGroup)
errors[field] = this.getErrors(control);
);
return errors;
// Calling it:
let formErrors = getErrors(this.form);
【讨论】:
【参考方案7】:我使用的是 Angular 5,您可以使用 FormGroup 例如简单地检查表单的状态属性
this.form = new FormGroup(
firstName: new FormControl('', [Validators.required, validateName]),
lastName: new FormControl('', [Validators.required, validateName]),
email: new FormControl('', [Validators.required, validateEmail]),
dob: new FormControl('', [Validators.required, validateDate])
);
除非所有字段都通过所有验证规则,否则 this.form.status 将为“INVALID”。
最好的部分是它可以实时检测变化。
【讨论】:
是的,但我们需要获取整个表单组的错误,而不仅仅是知道它是否无效 OP 需要验证消息,它不包含在 status 属性中,因为它只是一个布尔值。【参考方案8】:试试这个,它将为表单中的所有控件调用验证:
validateAllFormControl(formGroup: FormGroup)
Object.keys(formGroup.controls).forEach(field =>
const control = formGroup.get(field);
if (control instanceof FormControl)
control.markAsTouched( onlySelf: true );
else if (control instanceof FormGroup)
this.validateAllFormControl(control);
);
【讨论】:
【参考方案9】:对于大型 FormGroup 树,您可以使用 lodash 清理树并获得仅包含错误控件的树。这是通过重复子控件(例如使用allErrors(formGroup)
)并修剪任何完全有效的控件子组来完成的:
private isFormGroup(control: AbstractControl): control is FormGroup
return !!(<FormGroup>control).controls;
// Returns a tree of any errors in control and children of control
allErrors(control: AbstractControl): any
if (this.isFormGroup(control))
const childErrors = _.mapValues(control.controls, (childControl) =>
return this.allErrors(childControl);
);
const pruned = _.omitBy(childErrors, _.isEmpty);
return _.isEmpty(pruned) ? null : pruned;
else
return control.errors;
【讨论】:
【参考方案10】:这是另一种递归收集错误的变体,不依赖于任何外部库,如 lodash
(仅限 ES6):
function isFormGroup(control: AbstractControl): control is FormGroup
return !!(<FormGroup>control).controls;
function collectErrors(control: AbstractControl): any | null
if (isFormGroup(control))
return Object.entries(control.controls)
.reduce(
(acc, [key, childControl]) =>
const childErrors = collectErrors(childControl);
if (childErrors)
acc = ...acc, [key]: childErrors;
return acc;
,
null
);
else
return control.errors;
【讨论】:
【参考方案11】:基于@MixerOID 响应,这是我作为组件的最终解决方案(也许我创建了一个库)。我也支持FormArray的:
import Component, ElementRef, Input, OnInit from '@angular/core';
import FormArray, FormGroup, ValidationErrors from '@angular/forms';
import TranslateService from '@ngx-translate/core';
interface AllValidationErrors
controlName: string;
errorName: string;
errorValue: any;
@Component(
selector: 'app-form-errors',
templateUrl: './form-errors.component.html',
styleUrls: ['./form-errors.component.scss']
)
export class FormErrorsComponent implements OnInit
@Input() form: FormGroup;
@Input() formRef: ElementRef;
@Input() messages: Array<any>;
private errors: AllValidationErrors[];
constructor(
private translateService: TranslateService
)
this.errors = [];
this.messages = [];
ngOnInit()
this.form.valueChanges.subscribe(() =>
this.errors = [];
this.calculateErrors(this.form);
);
this.calculateErrors(this.form);
calculateErrors(form: FormGroup | FormArray)
Object.keys(form.controls).forEach(field =>
const control = form.get(field);
if (control instanceof FormGroup || control instanceof FormArray)
this.errors = this.errors.concat(this.calculateErrors(control));
return;
const controlErrors: ValidationErrors = control.errors;
if (controlErrors !== null)
Object.keys(controlErrors).forEach(keyError =>
this.errors.push(
controlName: field,
errorName: keyError,
errorValue: controlErrors[keyError]
);
);
);
// This removes duplicates
this.errors = this.errors.filter((error, index, self) => self.findIndex(t =>
return t.controlName === error.controlName && t.errorName === error.errorName;
) === index);
return this.errors;
getErrorMessage(error)
switch (error.errorName)
case 'required':
return this.translateService.instant('mustFill') + ' ' + this.messages[error.controlName];
default:
return 'unknown error ' + error.errorName;
还有 HTML:
<div *ngIf="formRef.submitted">
<div *ngFor="let error of errors" class="text-danger">
getErrorMessage(error)
</div>
</div>
用法:
<app-form-errors [form]="languageForm"
[formRef]="formRef"
[messages]="language: 'Language'">
</app-form-errors>
【讨论】:
【参考方案12】:从 Angular 表单中检索所有错误的递归方式,在创建任何类型的公式结构后,无法从表单中检索所有错误。这对于调试目的非常有用,而且对于绘制这些错误也非常有用。
针对 Angular 9 测试
getFormErrors(form: AbstractControl)
if (form instanceof FormControl)
// Return FormControl errors or null
return form.errors ?? null;
if (form instanceof FormGroup)
const groupErrors = form.errors;
// Form group can contain errors itself, in that case add'em
const formErrors = groupErrors ? groupErrors : ;
Object.keys(form.controls).forEach(key =>
// Recursive call of the FormGroup fields
const error = this.getFormErrors(form.get(key));
if (error !== null)
// Only add error if not null
formErrors[key] = error;
);
// Return FormGroup errors or null
return Object.keys(formErrors).length > 0 ? formErrors : null;
【讨论】:
我正在使用 Angular 7 并对您的代码进行了两次修改:form.errors ?? null
我必须删除 ??让它编译。更重要的是,在 FormGroup 检查条件中,我添加了|| formParameter instanceof FormArray
,它真正打开了我的应用程序。谢谢!
FormArray 怎么样?【参考方案13】:
它可能关注的对象 - 我对 Andreas 代码进行了调整,以便将所有错误代码放在一个平面对象中,以便更轻松地记录可能出现的错误。
请考虑:
export function collectErrors(control: AbstractControl): any | null
let errors = ;
let recursiveFunc = (control: AbstractControl) =>
if (isFormGroup(control))
return Object.entries(control.controls).reduce(
(acc, [key, childControl]) =>
const childErrors = recursiveFunc(childControl);
if (childErrors)
if (!isFormGroup(childControl))
errors = ...errors, [key]: childErrors ;
acc = ...acc, [key]: childErrors ;
return acc;
,
null
);
else
return control.errors;
;
recursiveFunc(control);
return errors;
【讨论】:
【参考方案14】:**I met the same problem and for finding all validation errors and
displaying only first error, I wrote next method:**
> first declare variable on top
public errors: any = [];
public fieldError: any = '';
> now subscribe form on noOnInit
this.form.valueChanges.subscribe(() =>
this.showOnlyFirstError(this.form);
this.errors = []
);
this.showOnlyFirstError(this.form);
> now call function
showOnlyFirstError(form)
Object.keys(form.controls).forEach(key =>
const controlErrors: ValidationErrors = form.get(key).errors;
if (controlErrors != null)
Object.keys(controlErrors).forEach(keyError =>
const showMessage = key + " is " + keyError
this.errors.push(showMessage)
this.fieldError = this.errors[0]
);
);
【讨论】:
【参考方案15】:我需要呈现包含 FormControls、FromGroups 和 FormArrays 的非常复杂的 FormGroup 控件的所有错误
我试图找到简单的解决方案,但我无法找到支持所有类型控件的完美解决方案,因此我开发了以下简单的递归函数,并与大家分享:
export interface FieldError
formGroupName: string;
fieldName: string;
errorCode: string;
export function getFormErrors(
control: AbstractControl,
formGroupName: string,
fieldName: string,
errors: FieldError[])
if (control instanceof FormGroup)
Object.keys(control.controls).forEach(controlName =>
let formControl = control.get(controlName);
if (formControl)
let fGroupName = formGroupName + "-" + controlName;
getFormErrors(formControl, fGroupName, controlName, errors);
)
if (control instanceof FormArray)
control.controls.forEach((fControl: AbstractControl, index) =>
let fGroupName = formGroupName + "-" + index;
getFormErrors(fControl, fGroupName, "Array", errors);
)
if (control instanceof FormControl)
const controlErrors: ValidationErrors | null = control.errors;
if (controlErrors)
Object.keys(controlErrors).forEach(errorCode =>
errors.push(
formGroupName: formGroupName,
fieldName: fieldName,
errorCode: errorCode
)
);
用法如下:
let errors: FieldError[] = []
getFormErrors(YOUR_FORM_GROUP, "root", "", errors);
【讨论】:
【参考方案16】:调整the accepted answer 以返回一个可以打印到控制台的字符串:
function getFormValidationErrors(form: FormGroup): string
return Object.keys(form.controls)
.map((control) =>
const controlErrors = form.get(control).errors;
if (!controlErrors)
return [];
const controlErrorsString = Object.keys(controlErrors)
.flatMap(
(keyError) => `$keyError: $controlErrors[keyError]`
)
.join(', ');
return `$control: $controlErrorsString`;
)
.filter((list) => list.length > 0)
.join('\n');
【讨论】:
以上是关于从 Angular 2 FormGroup 获取所有验证错误的主要内容,如果未能解决你的问题,请参考以下文章
无法从'@angular/forms'导入“导入FormGroup,FormControl [重复]
typescript Angular Service将FormGroup与LocalStorage同步(获取和设置)
Angular FormGroup valueChanges 将属性转换为数组