3

I'm migrating a project from angular2 RC4 to RC6 and I have a custom Form Validator which needs Http. Before the migration I used the ReflectiveInjector with the HTTP_PROVIDERS, but with RC6 this is not possible anymore as HTTP_PROVIDERS is deprecated, respectively not present anymore. This is the static method in the Validator:

    static checkVat(control: FormControl) {
    let checkVatUrl = "http://localhost:8080/checkvat";


    let injector = ReflectiveInjector.resolveAndCreate([HTTP_PROVIDERS]);
    let http = injector.get(Http);
    let authHttp = new AuthHttp(new AuthConfig(), http);

    if (control.value === "") {
        return new Observable((obs: any) => {
            obs.next(null);
            obs.complete();
        });
    } else {
        return authHttp.get(checkVatUrl + "/" + control.value)
            .map((data: Response) => {
                if (data.json().valid) {
                    return null;
                } else {
                    let reason = "isNotValidVat";
                    return {[reason]: true};
                }
            })
            .catch(function (e) {
                return new Observable((obs: any) => {
                    obs.complete();
                });
            });
    }
}

Just replacing HTTP_PROVIDERS with HttpModule didn't work, I found a similar problem here on stackoverflow (NG2 RC5: HTTP_PROVIDERS is deprecated) regarding testing, but the only answer is specific for testing.

How do I manually "inject" Http or HttpModule with RC6, if there is another or better solution for this custom Validator I'm open to that too.

Thanks in advance.

UPDATE: The checkVat method is static, that is why I had to use the ReflectiveInjector and not just inject it via the constructor, like everywhere else. The custom Validator gets used like this:

this.vatCtrl = new FormControl("", Validators.compose([Validators.pattern(this.vatService.vatPattern)]),VatValidator.checkVat);

UPDATE2: With the help of Günther Zöchbauer's answer I changed the Code as follows to get it working without a static function and no need for manual injection:

The Validator:

@Injectable()

export class VatValidator {

constructor(private http: Http) {
}

checkVat(control: FormControl) {

    let checkVatUrl = "http://localhost:8080/checkvat";

    let authHttp = new AuthHttp(new AuthConfig(), this.http);

    if (control.value === "") {
        return new Observable((obs: any) => {
            obs.next(null);
            obs.complete();
        });
    } else {
        return authHttp.get(checkVatUrl + "/" + control.value)
            .map((data: Response) => {
                if (data.json().valid) {
                    return null;
                } else {
                    let reason = "isNotValidVat";
                    return {[reason]: true};
                }
            })
            .catch(function (e) {
                return new Observable((obs: any) => {
                    obs.complete();
                });
            });
    }

}

}

In the component which has the FormControl:

    constructor(private vatValidator: VatValidator) {

    this.vatCtrl = new FormControl("", Validators.compose([Validators.pattern(vatPattern)]), this.vatValidator.checkVat.bind(this.vatValidator));

}
derpawe
  • 75
  • 9
  • Why do you need `let injector = ReflectiveInjector.resolveAndCreate([HTTP_PROVIDERS]);` instead of just `constructor(private http:Http){}`? – Günter Zöchbauer Sep 09 '16 at 08:20
  • Because `checkVat` is static. I will add the use of this method to my question. – derpawe Sep 09 '16 at 09:07
  • Why does it need to be static? – Günter Zöchbauer Sep 09 '16 at 09:14
  • The call of this method (shown in the Update of the question) doesn't work for me, if I make this function non-static and create an instance of the `VatValidator`. The parameter of `checkVat` is the FormControl the Validator is applied to, if I use an instance (`vatValidator`) and call the method like this: `vatValidator.checkVat(this.vatCtrl)` The compiler doesn't like that because of the parameter. This is a AsyncValidator. – derpawe Sep 09 '16 at 09:34

3 Answers3

12
import { ReflectiveInjector } from '@angular/core';
import { Http, XHRBackend, ConnectionBackend, BrowserXhr, ResponseOptions, XSRFStrategy, BaseResponseOptions, CookieXSRFStrategy, RequestOptions, BaseRequestOptions } from '@angular/http';

class MyCookieXSRFStrategy extends CookieXSRFStrategy {}

...

let http =  ReflectiveInjector.resolveAndCreate([
        Http, BrowserXhr, 
        { provide: ConnectionBackend, useClass: XHRBackend },
        { provide: ResponseOptions, useClass: BaseResponseOptions },
        { provide: XSRFStrategy, useClass: MyCookieXSRFStrategy },
        { provide: RequestOptions, useClass: BaseRequestOptions }
      ]).get(Http);

Sure, you still need HttpModule included, enjoy!

AlexV
  • 249
  • 3
  • 8
  • 2
    This worked great but very clunky. @AlexV have you brought this up to Anuglar team as feature request to improve on this and make it easier? – ScottN Oct 26 '16 at 18:11
  • 1
    Thanks for the answer. I needed this. Agree with @ScottN, wish there was a better way to just grab the Http service that's out there. – AMB0027 Nov 22 '16 at 15:38
  • 1
    Getting error: `Cannot resolve all parameters for 'MyCookieXSRFStrategy'` in Angular 4.0.0-rc3. Found solution here: http://stackoverflow.com/questions/39243525/inject-http-manually-in-angular-2 – Arg0n Mar 15 '17 at 11:29
2

If you change your validator class a bit, you don't need a static method

@Injectable()
class PatternValidator {
  constructor(private http:Http){}

  // this is a method that returns a validator function  
  // configured with a pattern
  pattern(pattern) {
    return (control:Control) => {
      this.http.get(...)

    ...
    }
  }
}

You can use it like:

  • inject it to your component so DI passes it's dependencies in (Http)
constructor(private pattern:PatternValidator) {}
  • pass it with bind(pattern) so .this keeps working inside the validator function
this.vatCtrl = new FormControl("", 
    Validators.compose([
        this.pattern(this.vatService.vatPattern).bind(this.pattern)
    ]), VatValidator.checkVat);

See also Inject Http manually in angular 2

Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Thanks, I will try this, although I need this for the Custom AsyncValidator `VatValidator.checkVat` and not the pattern-Validator, which is angular2 native and I'm not really sure yet how to use it with it in this code: `this.vatCtrl = new FormControl("", Validators.compose([ this.pattern(this.vatService.vatPattern).bind(pattern) ]), VatValidator.checkVat);` – derpawe Sep 09 '16 at 10:30
  • Sorry for mixing these up. Should work the same for `checkVat`. `this.vatCtrl = new FormControl("", Validators.compose([ this.pattern(this.vatService.vatPattern)]), this.vatValidator.checkVat.bind(this.vatValidator))` – Günter Zöchbauer Sep 09 '16 at 10:41
  • If you post more code (not in the comment - edit the question) I can provide more concrete feedback. – Günter Zöchbauer Sep 09 '16 at 10:42
  • Great, glad to hear :) – Günter Zöchbauer Sep 09 '16 at 10:59
0

With and after RC5 what you can do is,

import { HttpModule} from '@angular/http';
@NgModule({
  imports:      [ BrowserModule,HttpModule ],  //<------HttpModule
  declarations: [ AppComponent],
  providers:    [service],

  bootstrap:    [ AppComponent ]
})

And In service or component,

import { Http, Response } from '@angular/http';
@Injectable()
export class service{
  constructor(private http:Http){}           //<----inject here

  // use http here
}
micronyks
  • 54,797
  • 15
  • 112
  • 146