Home > Enterprise >  Reactive Forms and Custom errors on formControl
Reactive Forms and Custom errors on formControl

Time:07-07

I've been puzzled by the following issue;

I created a reactive form group with some props. Once these are filled out, I want to give the option for the API/backend to set an error message on one of the form controls, which all seems fine and works (see StackBlitz). https://stackblitz.com/edit/angular-ycvbgq?file=src/app/app.component.ts

HTML

    <form [formGroup]="form">
     <div  *ngIf="editMode | async">
      <input formControlName="userName" placeholder="first name" />
      <input formControlName="userLastName" placeholder="last name" />
     </div>
    <button  (click)="addError()">Error</button>
    </form>

TS

    addError() {
      this.form.controls['userName'].setErrors({
        backend: { message: 'My best wishes' },
      });
      this.form.updateValueAndValidity();
    }

So let's set the names to "Luke" and "Skywalker" and update the form to simulate the call. We get our errors object with whatever the backend wishes to say.

Now try toggling the editing button ("Stop Editing" and then "Edit Content"). As you can see, once you stop editing and then try to edit the content again, the "Errors" disappear. I thought the errors would need to be manually cleared?

So my question is how can I keep the Errors messages around or would I be forced to use AsyncValidators in this case?

CodePudding user response:

Destroying the input element then recreating it resets the form control. You can set them to display: none instead.

<div  [style.display]="(editMode | async) ? '' : 'none'">
      <input formControlName="userName" placeholder="first name" />
      <input formControlName="userLastName" placeholder="last name" />
</div>

Stackblitz: https://stackblitz.com/edit/angular-te4wb1?file=src/app/app.component.html

CodePudding user response:

To persist an error message like this you would be better off by passing the message into a CustomValidator, which would maintain the error message until a user approves the Validation or removes the validator. Also to remove the delay of displaying the error I'm setting the error object at the same time, just like I'm removing it too.

Solution 2, would be to do the same however then with an AsyncCustomValidator and a Service that holds all the messages with an id once the message with that id gets removed the Validator should return null and therefore pass and clear the error.

https://stackblitz.com/edit/angular-arbklk?file=src/app/app.component.ts

TS

    addError() {
     const msg = 'My best wishes';
     this.form.controls['userName'].setErrors({ backend: msg });
     this.form.controls['userName'].addValidators(apiMsgValidator(msg));
     this.form.updateValueAndValidity();
    }

Custom Validator

    export function apiMsgValidator(msg: string): ValidatorFn {
      return (control: AbstractControl): ValidationErrors | null => {
        const bakendMsg = true;
        return bakendMsg ? { backend: msg } : null;
      };
     }

By going about it this way the error doesn't clear away by any interaction until you remove the Validator. If the FormGroup were in a Service the user can also change routes while maintaining the error.

The main disadvantage with display:none would be that the errors will get cleared after touching any one of the controls or changing routes.

The new problem is how do you clear this specific error. As said above we give the user a control button. A reference to the Validator is going to be needed too for us to be able to remove the Custom Validator.

HTML

     <button
      *ngIf="form.controls.userName.errors?.backend?.id"
      
      (click)="removeValidator(form.controls.userName.errors?.backend?.id)"
    >
      {{ 'I have read and understood the error, clear error.' }}
    </button>

TS

      validations = {};

      addError() {
        const id = '17';
        const message = 'My best wishes';
        this.validations = {
          ...this.validations,
          [id]: apiMsgValidator(message, id),
        };
        this.form.controls['userName'].setErrors({ backend: { id, message } });
        this.form.controls['userName'].addValidators(this.validations[id]);
        this.form.updateValueAndValidity();
      }

      removeValidator(id: string) {
       this.form.controls['userName'].removeValidators(this.validations[id]);
       this.form.controls['userName'].setErrors({ backend: null });
       this.form.updateValueAndValidity();
      }
  • Related