How to add custom async validators in Angular

I cloned the Angular seed project and have implemented a suitable schema/uischema and some mock data.
However I’m struggling to figure out how to wire up a custom text input validator that must call a backend service to validate its input and display an error if invalid.
I tried creating a custom component by copying TextControlRenderer and adding the relevant parts from custom.autocomplete.ts, but there are several problems:

  1. this.form.valueChanges seems to generate events when editing all other inputs than the custom component (e.g. a TextControlRenderer)

  2. I cannot seem to reliably add an error to the component when the service call has been resolved and the result is an error. Sometimes the component is marked invalid (red underline) and sometimes not. Also the spinner is not showing up, or sometimes showing up and not disappearing.

What am I doing wrong?
Disclaimer: I’m pretty rusty with web dev
Thanks

My component:

import {ChangeDetectionStrategy, Component} from '@angular/core';
import {JsonFormsAngularService, JsonFormsControl} from '@jsonforms/angular';
import {ValidationService} from "./validation/validation.service";
import {debounceTime, finalize, switchMap, tap} from "rxjs/operators";

@Component({
  selector: 'MyTextControlRenderer',
  template: `
    <mat-form-field fxFlex [fxHide]="hidden">
      <mat-label>{{ label }}</mat-label>
      <input
        style="background-color: aquamarine"
        matInput
        [type]="getType()"
        (input)="onChange($event)"
        [id]="id"
        [formControl]="form"
      />
      <mat-option *ngIf="isLoading" class="is-loading">
        <mat-spinner diameter="30"></mat-spinner>
      </mat-option>
      <mat-hint *ngIf="shouldShowUnfocusedDescription()">{{ description }}</mat-hint>
      <mat-error>{{ error }}</mat-error>
    </mat-form-field>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyTextControlRenderer extends JsonFormsControl {
  isLoading: boolean;

  constructor(jsonformsService: JsonFormsAngularService, private validationService: ValidationService) {
    super(jsonformsService);
  }

  ngOnInit() {
    super.ngOnInit();
    console.log(JSON.stringify(this.form));
    this.form.valueChanges
      .pipe(
        tap(() => console.log("onChange")),
        debounceTime(300),
        tap(() => this.isLoading = true),
        switchMap(value => this.validationService.validateCareTeam(value)
          .pipe(
            finalize(() => {
              console.log("finalize")
              return this.isLoading = false;
            })
          )
        )
      )
      .subscribe(isValid => {
        console.log("next value: " + isValid)
        this.form.setErrors({invalid: true});
        //if (!isValid) this.error = "invalid"; <-- not working either
      });
  }

  getEventValue = (event: any) => event.target.value;
  getType = (): string => {
    if (this.uischema.options && this.uischema.options.format) {
      return this.uischema.options.format;
    }
    if (this.scopedSchema && this.scopedSchema.format) {
      switch (this.scopedSchema.format) {
        case 'email':
          return 'email';
        case 'tel':
          return 'tel';
        default:
          return 'text';
      }
    }
    return 'text';
  };
}

My service class:

export class ValidationService {

  constructor(private http: HttpClient) { }

  validateCareTeam(ref: String): Observable<boolean> {
    // return this.http.get<boolean>('./asdf');
    console.log("service called")
    return of(false).pipe(delay(1000));
  }
}

[original thread by Tue Toft Nørgård]

Hi @tue-cn(tue-cn), to show custom errors you don’t need your own renderer. You can send the data to validate to your service, and, once complete, you can set an appropriate error in JSON Forms. Via the error path you can control where the error shall be shown. For this you can use the updateErrors action which you can execute against the core in the JsonFormsAngularService.

[Fioyesuraj]

hi @sdirix(sdirix) , Can you show me example how to use the updateErrors in angular …

Hi @fioyesuraj(fioyesuraj), just to be clear: In general we don’t recommend setting custom errors but adding them via Ajv customization. Only if that really isn’t applicable you might want to use updateErrors.

If you really need to do it you can for example execute this.jsonFormsService.updateCore(Actions.updateErrors(newErrors)); within a renderer.

[Fioyesuraj]

hi @sdirix(sdirix) i have update the errors based on your suggestion …i can able to get value only at the time of code hit in debugger (this.error) after code run in html {{ error}} i could not get the value …Meanwhile when error is thrown save button should not allow to submit …

You can listen to the emitted errors via the root json forms component. When there are some you can disable your save button.

[Fioyesuraj]

root json forms component means? i could not get you properly … can you explain clearly?

In your Angular application you need to invoke JSON Forms at some place. In the Angular seed it is the app component. There you can not only bind to the data but also errors are emitted.

[Fioyesuraj]

Hi Team ,

[Fioyesuraj]

Thanks for the update … I got it … I have another question …how to remove the updated errors …for eg.i want to empty this.jsonformsService.getState().jsonforms.core.errors=[]; ho can i achieve this