鉴于此代码:
this.form = this.formBuilder.group({
email: ['', [Validators.required, EmailValidator.isValid]],
hasAcceptedTerms: [false, Validators.pattern('true')]
});
如何从this.form
获取所有验证错误?
我正在编写单元测试,并希望在断言消息中包含实际的验证错误。
答案 0 :(得分:62)
我遇到了同样的问题,为了找到所有验证错误并显示它们,我写了下一个方法:
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(..)
替换为您需要的内容。
答案 1 :(得分:19)
这是支持内部FormGroup
的解决方案( like here )
测试:Angular 4.3.6
获取形式的验证-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;
}
答案 2 :(得分:6)
或者您可以使用此库来获取所有错误,即使是深层和动态表单也是如此。
npm install --save npm i @naologic/forms
你想要
import {SuperForm} from 'npm i @naologic/forms';
然后只需传递FormGroup即可获取所有错误
const errorsFlat = SuperForm.getAllErrorsFlat(fg);
console.log(errorsFlat);
更新:
您可以使用npm i @naologic/forms
并使用NaoFromGroup
代替角度的经典表单组。它有一些新功能,你也可以得到所有错误。
抱歉,目前还没有文档,只需查看代码即可。
更新:
lib需要angular 6,因为它使用AbstractControlOptions
答案 3 :(得分:6)
从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;
}
}
答案 4 :(得分:3)
这是另一个变种,它以递归方式收集错误,并且不依赖于任何外部库,例如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;
}
}
答案 5 :(得分:1)
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拿走了它并对其进行了一些修改。
答案 6 :(得分:1)
试试这个,它会调用所有控件的验证形式:
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.validateAllFormFields(control);
}
});
}
答案 7 :(得分:1)
基于响应,这是我作为组件的最终解决方案(也许我创建了一个库)。我也支持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>
答案 8 :(得分:0)
您可以迭代this.form.errors属性。
答案 9 :(得分:0)
// 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);
答案 10 :(得分:0)
对于大型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;
}
}
答案 11 :(得分:0)
它可能关心的人 - 我调整了 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;
}
答案 12 :(得分:0)
**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]
});
}
});
}
答案 13 :(得分:-1)
我正在使用angular 5,您只需使用FormGroup检查表单的status属性,例如
get_string
this.form.status将是“INVALID”,除非所有字段都通过了所有验证规则。
最好的部分是它实时检测变化。