Biorąc pod uwagę ten kod:
this.form = this.formBuilder.group({
email: ['', [Validators.required, EmailValidator.isValid]],
hasAcceptedTerms: [false, Validators.pattern('true')]
});
Jak mogę uzyskać wszystkie błędy walidacji z this.form
?
Piszę testy jednostkowe i chcę uwzględnić rzeczywiste błędy walidacji w komunikacie potwierdzenia.
angular
typescript
validation
EagleBeak
źródło
źródło
Odpowiedzi:
Spotkałem ten sam problem i aby znaleźć wszystkie błędy walidacji i wyświetlić je napisałem następną metodę:
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]); }); } }); }
Nazwa formularza
productForm
powinna zostać zmieniona na twoją.Działa to w następujący sposób: wszystkie nasze kontrolki pobieramy z formularza w formacie
{[p: string]: AbstractControl}
i iterujemy po każdym kluczu błędu, aby uzyskać szczegółowe informacje o błędzie. Pomijanull
wartości błędów.Można go również zmienić, aby wyświetlać błędy walidacji w widoku szablonu, po prostu zamień
console.log(..)
na to, czego potrzebujesz.źródło
' + controlErrors[keyErrors];
zamiast', controlErrors[keyErrors];
?ValidationErrors
w Angular 2?import { ValidationErrors } from '@angular/forms';
To rozwiązanie ze
FormGroup
wspornikami wewnętrznymi ( jak tutaj )Testowano na: kątowe 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; }
Na przykładzie :
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; }
źródło
controlErrors
tj.if (controlErrors) {
Jako sprawdzanie tylko pod kątemnull
spowoduje błąd, jeśli wystąpią błędyundefined
Jest to kolejny wariant, który zbiera błędy rekurencyjnie i nie zależy od żadnej zewnętrznej biblioteki, takiej jak
lodash
(tylko 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; } }
źródło
Rekurencyjny sposób pobierania wszystkich błędów z formularza Angular , po utworzeniu dowolnej struktury formułowej nie ma możliwości odzyskania wszystkich błędów z formularza. Jest to bardzo przydatne do celów debugowania, ale także do wykreślania tych błędów.
Przetestowano pod kątem 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; } }
źródło
form.errors ?? null
musiałem usunąć ?? do kompilacji. Co ważniejsze, w warunku sprawdzania FormGroup dodałem,|| formParameter instanceof FormArray
co naprawdę otworzyło moją aplikację. Dzięki!Lub możesz po prostu użyć tej biblioteki, aby uzyskać wszystkie błędy, nawet z głębokich i dynamicznych formularzy.
Jeśli chcesz używać funkcji statycznej we własnych formularzach
import {NaoFormStatic} from '@naologic/forms'; ... const errorsFlat = NaoFormStatic.getAllErrorsFlat(fg); console.log(errorsFlat);
Jeśli chcesz użyć,
NaoFromGroup
możesz go zaimportować i używać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"}}
Przeczytaj pełną dokumentację
źródło
Na podstawie odpowiedzi @MixerOID , oto moje ostateczne rozwiązanie jako komponent (może utworzę bibliotekę). Wspieram również 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; } } }
A HTML:
<div *ngIf="formRef.submitted"> <div *ngFor="let error of errors" class="text-danger"> {{getErrorMessage(error)}} </div> </div>
Stosowanie:
<app-form-errors [form]="languageForm" [formRef]="formRef" [messages]="{language: 'Language'}"> </app-form-errors>
źródło
Spróbuj tego, wywoła walidację dla całej kontroli w postaci:
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); } }); }
źródło
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; } }
Wziąłem to od Deborahk i trochę zmodyfikowałem.
źródło
// 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);
źródło
Możesz iterować po właściwości this.form.errors.
źródło
this.form.errors
zwraca to tylko błędy walidacji dlathis.form
, a nie dlathis.form.controls
. Możesz osobno zweryfikować FormGroups i jego elementy podrzędne (dowolną liczbę FormGroups, FormControls i FormArrays). Myślę, że aby pobrać wszystkie błędy, musisz zadawać je rekurencyjnie.W przypadku dużego drzewa FormGroup można użyć lodash, aby wyczyścić drzewo i uzyskać drzewo tylko formantów z błędami. Odbywa się to poprzez powtarzanie kontroli podrzędnych (np. Przy użyciu
allErrors(formGroup)
) i przycinanie wszelkich w pełni ważnych podgrup kontroli: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; } }
źródło
Używam Angular 5 i możesz po prostu sprawdzić właściwość statusu swojego formularza za pomocą FormGroup np
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 miałoby wartość „INVALID”, chyba że wszystkie pola spełniają wszystkie reguły walidacji.
Najlepsze jest to, że wykrywa zmiany w czasie rzeczywistym.
źródło