Async validation message not displaying in ngx-formly

527 views Asked by At

When trying this example from the ngx-formly library, the message that comes from the async validation is not displaying under the field as exprected

https://formly.dev/docs/examples/validation/unique-value-async-validation/

When reducing the timeout in the async validator expression, I see that the async validator is called instantly, but the message: 'This username is already taken.' is not displayed under the field !

Here is the code from the stackblitz link:

Template

<b>Existing usernames</b>
<ul>
  <li *ngFor="let u of existingUsers">{{ u }}</li>
</ul>

<form [formGroup]="form" (ngSubmit)="submit()">
  <formly-form [model]="model" [fields]="fields" [options]="options" [form]="form"></formly-form>
  <button type="submit" class="btn btn-primary submit-button">Submit</button>
</form>

component class

import { Component } from '@angular/core';
import { FormGroup, AbstractControl } from '@angular/forms';
import { FormlyFormOptions, FormlyFieldConfig } from '@ngx-formly/core';
import { of } from 'rxjs';

@Component({
  selector: 'formly-app-example',
  templateUrl: './app.component.html',
})
export class AppComponent {
  form = new FormGroup({});
  model: any = {};
  options: FormlyFormOptions = {};

  existingUsers = ['user1', 'user2', 'user3'];

  fields: FormlyFieldConfig[] = [
    {
      key: 'username1',
      type: 'input',
      props: {
        label: 'Username (validated using `Promise`)',
        placeholder: 'Username',
        required: true,
      },
      asyncValidators: {
        uniqueUsername: {
          expression: (control: AbstractControl) => {
            return new Promise((resolve) => {
              setTimeout(() => {
                resolve(this.existingUsers.indexOf(control.value) === -1);
              }, 0);
            });
          },
          message: 'This username is already taken.',
        },
      },
    },
    {
      key: 'username2',
      type: 'input',
      props: {
        label: 'Username (validated using `Observable`)',
        placeholder: 'Username',
        required: true,
      },
      asyncValidators: {
        uniqueUsername: {
          expression: (control: AbstractControl) => {
            return of(this.existingUsers.indexOf(control.value) === -1);
          },
          message: 'This username is already taken.',
        },
      },
    },
  ];

  submit() {
    if (this.form.valid) {
      alert(JSON.stringify(this.model));
    }
  }
}

module configuration:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { FormlyModule } from '@ngx-formly/core';
import { FormlyBootstrapModule } from '@ngx-formly/bootstrap';
import { AppComponent } from './app.component';

@NgModule({
  imports: [
    BrowserModule,
    ReactiveFormsModule,
    FormlyBootstrapModule,
    FormlyModule.forRoot({
      validationMessages: [{ name: 'required', message: 'This field is required' }],
    }),
  ],
  bootstrap: [AppComponent],
  declarations: [AppComponent],
})
export class AppModule {}

Stackblitz link: https://stackblitz.com/edit/angular-f3fx7j?file=src%2Fapp%2Fapp.component.ts,src%2Fapp%2Fapp.module.ts

Question: is there any way to display the async validation message whenever the validation is not working.

Thanks

1

There are 1 answers

2
Umair Rasheed On

Step 01 In order to display this message to the user, you'll need to subscribe to the observable and set the error message on the form control.

import { FormControl } from '@angular/forms';
import { Observable } from 'rxjs';
export function asyncValidator(control: FormControl): Observable<any> {
  return new Observable(observer => {
    // simulate an asynchronous validation
    setTimeout(() => {
      if (control.value === 'test') {
        observer.next({ asyncValidation: 'Validation failed' });
      } else {
        observer.next(null);
      }
      observer.complete();
    }, 1000);
  });
}

Step 2 To use this validator with ngx-formly, you can define it as part of the field configuration

{
  key: 'test',
  type: 'input',
  templateOptions: {
    label: 'Test',
  },
  validators: {
    async: asyncValidator,
  },
  validation: {
    messages: {
      asyncValidation: 'Async validation failed',
    },
  },
},