2

I made a stackblitz with my issue I used templating to show the errors, when I do form.errors it returns null even though form.get('oldPassword').errors isn't returning null

EDIT
as a bonus I'm getting errors when I'm defining a getter for each field

get oldPassword() {
   return this.form.get('oldPassword')
}

EDIT

Here is my code working

4 Answers4

2

Check this feature request here: https://github.com/angular/angular/issues/10530

The FormGroup errors object will be not null, only when a FormGroup validator returns an error and not for control validators.

Consider a scenario where both values are ok but their combination is not. That's where the FormGroup validator could return something like PasswordAndVerificationAreNotEqual.

Check Get all validation errors from Angular 2 FormGroup to get all form group errors solution.

Athanasios Kataras
  • 25,191
  • 4
  • 32
  • 61
1

You problem is the setTimeout in the validator:

    static passwordValidator(control: AbstractControl): Promise<ValidationErrors | null> {

        return new Promise((resolve, reject) => {
        ==>    setTimeout(() => {
                if (control.value === "1234") {
                    resolve({ passwordValidator: true })
                }
                resolve(null)
            }, 2000);
        });


    }

setTimeout bypasses Angulars ChangeDetection, therefore the view is not informed about the validation error.

Therefore you should always use Observables in angular there is hardly a case where you need promises... and by using an observalbe you could delay the detection by piping the timeout and the form validation should work properly

Update: Your password validator should look like this:

export class CustomValidators {
    static passwordValidator(control: AbstractControl): Observable<ValidationErrors | null> {
        return of(control.value)
          .pipe(
            debounceTime(2000),
            distinctUntilChanged(),
            map(currentValue => currentValue === "1234" ? { passwordValidator: true } : null )
          )
    }
}

https://stackblitz.com/edit/angular-1su3cm

Nickolaus
  • 4,785
  • 4
  • 38
  • 60
  • That makes sense however, I just removed the async validator and the form.errors is still returning null while the forms.get('oldPassword'). errors is returning an object, when they should be the same; why? – Raphael Castro Nov 15 '19 at 17:18
  • @RaphaelCastro `*ngIf="form.get('oldPassword').errors && ..."` must be `*ngIf="form.get('oldPassword').errors.length > 0 && ..."` – Nickolaus Nov 15 '19 at 17:24
1

Angular doesn't expose control errors to FormGroup https://github.com/angular/angular/issues/10530 But it does handle invalid state for FormGroup.

In order to recursively get all errors I would offer you using some function like:

export function collectErrors(control: any): any | null {
  if (control.controls) {
    return Object.entries(control.controls).reduce(
      (acc, [key, childControl]) => {
        const childErrors = collectErrors(childControl);
        if (childErrors) {
          acc = { [key]: childErrors, ...acc };
        }
        return acc;
      },
      null
    );
  } else {
    return control.errors;
  }
}

then you can use it as follows:

onClick() {
  console.log(collectErrors(this.form));
  console.log(this.form.get("oldPassword").errors);
}

Stackblitz Example

yurzui
  • 205,937
  • 32
  • 433
  • 399
  • Lots of great answers, however this one gave me the idea to use the invalid state to disable the submit button which is what I wanted to do orignally – Raphael Castro Nov 15 '19 at 17:31
  • 1
    way to complicated => `Angular doesn't expose control errors to FormGroup` of course it does – Nickolaus Nov 15 '19 at 17:39
  • @Nickolaus then why is there an open issue that says it doesn't? At least not in an easy to access fashion. - https://github.com/angular/angular/issues/10530 What is your simpler solution to access all errors for all controls within a form? – rooby Oct 13 '21 at 02:02
1

for sure i am with nickolaus you should use observables it is much better if you cant refactor your app then use this

in html

<div *ngFor="let obj of getErrorList(form.controls)">
      <p>FormGroup errors: {{ obj | json }}</p>
    </div>

in ts

getErrorList(errorObject) {
  let errors = [] ;
  for(let key in errorObject){
    let obj={
      name:key,
      errors:errorObject[key].errors
    }
    errors.push(obj);
  }
  console.log(errors);
  return errors;
}
youssef ali
  • 406
  • 5
  • 11