1

I'm doing a web application in Angular 8.3.4. I have a form using Reactive Forms and I want to add Async Validation to make sure that the name entered is unique. I have read a lot of examples out there, but for some reason is not working for me.

Service

export class BrandService {
  brands: Observable<BrandId[]>;
}

Component

export class CreateNewComponent {

  form: FormGroup;

  constructor(
    private formBuilder: FormBuilder,
    private brandService: BrandService
  ) {
    // Creates a form group
    this.form = this.formBuilder.group({
      name: ['', Validators.compose([
        Validators.required,
        Validators.minLength(6),
        CustomValidator.unique(this.brandService.brands)
      ])]
    });
  }
}

Custom validator

export class CustomValidator {

  static unique(brands: Observable<BrandId[]>) {
    return (control: AbstractControl) => {

      const name = control.value.toLowerCase();

      return brands.pipe(
        map(items => items.filter(b => b.name.toLowerCase() === name)),
        map(items => items.length ? {unique: false} : null)
      );

    };
  }

}

I'm passing the Observable of the service from the component to the CustomValidator. Right now, the control has: status: "INVALID"

By the way, what should I explicitly return in my unique method?

My goal is to know if the name already exists in the array of the Observable to return a control error.

1
  • 1
    an async validator must be implements AsyncValidator, see angular.io/guide/…. NOTE: you always can in submit call to your observable and show a message it's not unique Commented Oct 1, 2019 at 14:54

1 Answer 1

0

The async validator should be passed in via another array (or on its own), something like

name: [
  '', 
  Validators.compose([ Validators.required, Validators.minLength(6)]), 
  () => CustomValidator.unique(this.brandService.brands)
]

Also notice that it is in a function, many of the examples I found early on used bind like in this article, but I like using a function instead.

------Edit ------ Here is a working example

@Component({
  selector: 'app-new',
  templateUrl: './new.component.html',
  styleUrls: ['./new.component.css']
})
export class NewComponent {

  form: FormGroup;

  constructor(
    private formBuilder: FormBuilder,
    private brandService: BrandService,
  ) {
    const brand = new BrandId();
    brand.name = 'abcdef';

    this.brandService.brands = of([brand]);
    // Creates a form group
    this.form = this.formBuilder.group({
      name: ['', Validators.compose([
        Validators.required,
        Validators.minLength(6),
      ]),
        (control) => CustomValidator.unique(this.brandService.brands, control)]
    });
  }
}

export class CustomValidator {
  static unique(brands: Observable<BrandId[]>, control: AbstractControl) {
    const name = control.value.toLowerCase();

    return brands.pipe(
      map(items => items.filter(b => b.name.toLowerCase() === name)),
      map(items => items.length ? { unique: false } : null)
    );

  }
}
Sign up to request clarification or add additional context in comments.

5 Comments

This is the only change that I need? According to the answer of Eliseo above, I need to implement AsyncValidator, this implementation is required or with your solution is enough?
In my testing as long as you return null it won't show an error. The error object you return looks like it should work.
Actually, I take that back. Try returning {unique: true}. At that point you may want to rename from unique.
For some reason, is still not working for me. The control/form keeps status: "INVALID" all the time.
Hi my friend, I have tried your new solution, but, it's not working for me yet. The field doesn't fire up the error message, I cannot tell if the value entered is unique or not. I don't know what is happening.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.