import { Injectable } from '@angular/core' ; import { FormGroup , AbstractControl , FormArray , FormControl , ValidationErrors } from '@angular/forms' ; import { debounceTime , distinctUntilChanged } from 'rxjs/operators' ; export declare interface ServerError { [ key ] : [ ] ; } @ Injectable ( { providedIn : 'root' } ) export class FormErrorHandlerService { private form : FormGroup ; private serverError : any ; private errorObject : ServerError ; private message : string ; constructor ( ) { } private static hasError ( control ) { return control . invalid && ( control . dirty || control . touched ) ; } param serverError param form public organizeServerErrors ( serverError , form ) { if ( serverError && typeof serverError === 'object' ) { this . form = form ; this . serverError = serverError ; this . setErrorToFormFields ( ) ; this . findErrors ( form . controls ) ; } } param form param errorObject public handleErrors ( form , errorObject ) { this . form = form ; this . errorObject = errorObject ; form . valueChanges . pipe ( debounceTime ( 500 ) , distinctUntilChanged ( ) ) . subscribe ( ( ) => { if ( form . invalid ) { this . findErrors ( form . controls ) ; } } ) ; } /** Used to call when an invalid form has been submit
     *  By setting error we emit a status changed event for the form.
     */ public invalidFormHasSubmit ( ) { this . form ?. markAllAsTouched ( ) ; this . form ?. setErrors ( { } ) ; } /** Finds appropriate fields on form and set's the server error. */ private setErrorToFormFields ( ) { Object . keys ( this . serverError ) . forEach ( field => { if ( ! Array . isArray ( this . serverError [ field ] ) ) { this . setErrorsToNestedFields ( field ) ; } else { const errorMessages = this . serverError [ field ] ; this . form . get ( field ) ?. setErrors ( { serverError : errorMessages [ 0 ] . message } ) ; } } ) ; } param nestedFieldName private setErrorsToNestedFields ( nestedFieldName ) { const nestedFormErrors = this . serverError [ nestedFieldName ] ; Object . keys ( nestedFormErrors ) . forEach ( field => { const formControl = this . form . get ( field ) ; if ( formControl ) { formControl . setErrors ( { serverError : nestedFormErrors [ field ] } ) ; } else { const nestedForm = this . form . get ( nestedFieldName ) ; const nestedField = nestedForm . get ( field ) ; if ( nestedField ) { nestedField . setErrors ( { serverError : nestedFormErrors [ field ] } ) ; } } } ) ; } param controls private findErrors ( controls ) { Object . keys ( controls ) . forEach ( ( control ) => { if ( controls [ control ] instanceof FormArray ) { Object . defineProperty ( this . errorObject , control , { value : [ ] , writable : true } ) ; this . findErrorsOnFormArrays ( controls [ control ] as FormArray , control ) ; } else if ( controls [ control ] instanceof FormControl ) { this . findErrorsOnFormControls ( controls , control ) ; } } ) ; } private findErrorsOnFormArrays ( formArray , formArrayName ) { let i = 0 ; for ( const formGroup of formArray . controls as FormGroup [ ] ) { const controls = formGroup . controls ; const formArrayErrors = this . errorObject [ formArrayName ] ; formArrayErrors . push ( { } ) ; Object . keys ( controls ) . forEach ( control => { if ( FormErrorHandlerService . hasError ( controls [ control ] ) ) { this . setErrorMessage ( controls [ control ] . errors ) ; Object . defineProperty ( formArrayErrors [ i ] , control , { value : this . message , writable : true } ) ; } } ) ; i ++ ; } } private findErrorsOnFormControls ( controls , control ) { if ( FormErrorHandlerService . hasError ( controls [ control ] ) ) { this . setErrorMessage ( controls [ control ] . errors ) ; this . setErrorToErrorObject ( control ) ; } } param errors private setErrorMessage ( errors ) { if ( errors . serverError ) { this . message = errors . serverError ; } else if ( errors . required ) { this . message = 'Required field' ; } else if ( errors . minlength ) { this . message = `Max length is ${ errors . minlength . actualLength }/${ errors . minlength . requiredLength }` ; } else if ( errors . maxlength ) { this . message = `Min length is ${ errors . maxlength . actualLength }/${ errors . maxlength . requiredLength }` ; } else if ( errors . email ) { this . message = 'Email is not valid' ; } else if ( errors . min ) { this . message = `Min value is ${ errors . min . min }, actual value is ${ errors . min . actual }` ; } else if ( errors . max ) { this . message = `Max value is ${ errors . max . max }, actual value is ${ errors . max . actual }` ; } else if ( errors . pattern ) { this . message = 'Invalid value' ; } else if ( errors . passwordMismatch ) { this . message = 'Passwords do not match' ; } else { this . message = '' ; } } param field private setErrorToErrorObject ( field ) { Object . defineProperty ( this . errorObject , field , { value : this . message , writable : true } ) ; } }