26

1. Is it even supported by Angular yet ? see this open issue

2. If it is, then what is wrong in the code below

export class someClass{

    myForm:ControlGroup;

    constructor(public http:Http, public formBuilder:FormBuilder)
       this.myForm = formBuilder.group({
            ImageId: ["", Validators.required, this.asynValidator]
    });

    asyncValidator(control: Control): {[key: string]: any} {

        return new Promise (resolve => {

          let headers = new Headers();
          headers.append('Content-Type', 'application/json');

          this.http.get('http://localhost/ImageIdValidate?id='+ control.value, {headers:headers})
                .map(res => res.json())
                .subscribe(data => {
                    console.log(data);
                    if(data != null) {
                        resolve({"duplicate": true})
                    }
                    else resolve(null);      
                })
            });
        });
      }
    }

It doesn't even make a server request.

AT82
  • 71,416
  • 24
  • 140
  • 167
Ankit Singh
  • 24,525
  • 11
  • 66
  • 89

3 Answers3

29

You need to bind your method on the component instance itself as described below:

this.myForm = formBuilder.group({
            ImageId: ["",    
               Validators.required, 
               this.asynValidator.bind(this)]
    });

Otherwise you won't be able to use the http property to execute your request.

This article could also give you some hints about asynchronous form validation (see the section "asynchronous validation"):

Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • 7
    In fact, it's the way JavaScript works. When you reference a method you lose the object it's attached on. You can force this using the bind method... – Thierry Templier Feb 20 '16 at 10:42
  • 1
    When I start to think that I'm on my way to be a PRO someday, things like this make me laugh. But anyway, is [this question](http://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-context-inside-a-callback) related ? – Ankit Singh Feb 20 '16 at 10:56
  • 1
    In fact OOP in JavaScript is different front the ones of languages like Java and C++. With the class keyword, you don't really define "true" classes like with languages. JavaScript usés prototype-based OOP. Yes the question you quoted is related to this problem. – Thierry Templier Feb 21 '16 at 07:16
  • And @ThierryTemplier's other answer [showing an alternative to `.bind()` using a closure](http://stackoverflow.com/questions/38900747/http-doesnt-work-in-angular-2-custom-asynchronous-validation). – msanford Oct 25 '16 at 15:59
3

as of newer versions of Angular, but pre version 5.0.0 you would add the async validator as the third argument for your formcontrol:

myControl: ['', [Validators.required], [this.asyncValidator.bind(this)]]

since version 5.0.0 you can now mark the validators like so:

myControl: ['', {validators: [Validators.required], 
                 asyncValidators:[this.asyncValidator.bind(this)]}]
AT82
  • 71,416
  • 24
  • 140
  • 167
  • I just implemented an async validation function, and I also had to add `NG_ASYNC_VALIDATORS` provider to the component! (Which is an annoying bit of magic!!) – Ian Grainger May 06 '21 at 14:17
0

Hello guys thanks for the solution. However it didnt work for me outta the box.

the problem was the async validator had to be a next parameter as part of the validators. so what worked for me was

this.myForm = formBuilder.group({
        ImageId: ["",    
           [Validators.required], 
           [this.asynValidator.bind(this)]]
});

and tadaa!! the headache was gone. hope it helps someone.

  • The only difference i can see is you added some extra [] comparing to the answer post. And you have not mentioned why is the extra [] require. This is not a helpful anser – MJK Aug 16 '17 at 11:39
  • The extra [] brackets are required because async validators now have to be listed as the third parameter. Sync validators are listed in the second parameter, async validators in the third. – Derrick Miller Aug 14 '18 at 02:02