web-dev-qa-db-ja.com

静的メソッドまたはカスタムクラスにHttpClientを挿入する方法は?

静的メソッドまたはクラスでangular HttpClientを使用したい(クラスではコンストラクタパラメータとして定義できない)。

私は次のようなことを試しました:

export class SomeNotInjectableService {
  static doSomething() {
    const injector = Injector.create({
      providers: [{provide: HttpClient, deps:[]}]
    });
    const httpClient: HttpClient = injector.get(HttpClient);

    httpClient.request(...); // error Cannot read property 'handle' of undefined
  }
}

これは、静的サービスメソッドでクライアントを手動で挿入する試みです。動作しません。これを行う方法、またはコンポーネントではないクラスで通常のメソッドにクライアントを挿入する方法に興味があります。

14
elzoy

私がそれがあなたが試した方法で動作しない(おそらくインジェクターを作成するときに何かが足りない)かどうかは正確にはわかりませんが、「注入された」インジェクターを使用すると機能します

エラーをスローするコードを見ると、リクエストのハンドラーが記述されていることがわかります。これは、例ではnullのようです。たぶんangularは、HttpClientが「従来の」方法で提供されている場合、一部の内部ハンドラーを登録しますが、その方法は登録しません

// Observable.of()で最初のリクエストを開始し、concatMap()内でハンドラー(//すべてのインターセプターを含む)を実行します。このようにして、ハンドラーは監視可能なチェーン内で//実行されます。これにより、サブスクリプションごとにインターセプターが再実行されます(これにより、インターセプターを含むハンドラーが再試行されます)。

    var /** @type {?} */ events$ = rxjs_operator_concatMap.concatMap.call(rxjs_observable_of.of(req), function (req) { return _this.handler.handle(req); });

回避策:

app.module.ts

import {Injector} from '@angular/core';

export let InjectorInstance: Injector;

export class AppModule 
{
  constructor(private injector: Injector) 
  {
    InjectorInstance = this.injector;
  }
}

あなたの静的クラス/メソッド

import {InjectorInstance} from './app.module';

export class SomeNotInjectableService {
  static doSomething() 
  {
  /*  const injector = Injector.create({
      providers: [{provide: HttpClient, deps:[]}]
    });
    const httpClient: HttpClient = injector.get(HttpClient);
*/
    const httpClient =  InjectorInstance.get<HttpClient>(HttpClient);

    httpClient.request(...)...
  }
}

Stackblitzの例: https://stackblitz.com/edit/angular-li8b37?file=app%2Fapp.component.ts

18
David

インジェクターがない場合は、スキップすることもできます。それは自分で「注入」を行うことを意味します。これはお勧めしません。 (適切なサービスを優先して)静的メソッドを本当に使用したい場合は、必要なものをすべて渡します。

これがまだ明確でないかどうかはわかりませんが、解決する方法がないため、このhttpClientパイプラインからHTTPインターセプターが失われます。

import { HttpClient, HttpXhrBackend } from '@angular/common/http';

const httpClient = new HttpClient(new HttpXhrBackend({ build: () => new XMLHttpRequest() }));
httpClient.get('test').subscribe(r => console.log(r));

または独自に作成したインジェクタを使用する(ctor argsを渡したくない場合):

const injector = Injector.create({
    providers: [
        { provide: HttpClient, deps: [HttpHandler] },
        { provide: HttpHandler, useValue: new HttpXhrBackend({ build: () => new XMLHttpRequest }) },
    ],
});
const httpClient: HttpClient = injector.get(HttpClient);
httpClient.get('test').subscribe(r => console.log(r));
17
Andrei Tătar

アンドリューの答えに基づいています。このhttpClientパイプラインでインターセプターを使用する場合は、angular repo http/src/interceptor.tsおよびhttp/src/module.tsから2つの再定義されたクラスを追加します。

class HttpInterceptorHandler implements HttpHandler {
  constructor(private next: HttpHandler, private interceptor: HttpInterceptor) {}

  handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
      return this.interceptor.intercept(req, this.next);
  }
}
class HttpInterceptingHandler implements HttpHandler {
  private chain: HttpHandler|null = null;
  private httpBackend:HttpHandler;
  constructor(private injector: Injector) {
      this.httpBackend = new HttpXhrBackend({ build: () => new XMLHttpRequest });
  }

  handle(req: HttpRequest<any>): Observable<HttpEvent<any>> {
      if (this.chain === null) {
          const interceptors = this.injector.get(HTTP_INTERCEPTORS, []);
          this.chain = interceptors.reduceRight((next, interceptor) => new HttpInterceptorHandler(next,interceptor),this.httpBackend);
      }
      return this.chain.handle(req);
    }
}

インターセプターは@Injectableデコレーターなしで必要です:

class HttpIntersept implements HttpInterceptor{
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
      console.log(req.urlWithParams);
      return next.handle(req)
  }
}

アンドリューのように言った

const injector = Injector.create({
providers: [
    { provide: HTTP_INTERCEPTORS, useClass: HttpIntersept, multi: true, deps: []},
    { provide: HTTP_INTERCEPTORS, useClass: HttpIntersept2, multi: true, deps: []},
    { provide: HttpHandler, useClass:HttpInterceptingHandler,deps [Injector,HTTP_INTERCEPTORS]},
    { provide: HttpClient, deps: [HttpHandler] }
 ],
});

必要なサービス/オブジェクトをパラメーターとして渡すと、非常に役立ちます。さらに、テストとコードの「読みやすさ」を支援します。この次のソリューションは、注入しようとしているすべてのタイプのオブジェクトで機能します。そして、少なくとも、あなたはそれを必要な場所/時に注入します。呼び出し側のオブジェクトは、必要なオブジェクトを注入する責任があります。

export class SomeNotInjectableService {
  static doSomething(injected: any) {
    httpClient = injected as HttpClient;
    if(httpClient) {
       httpClient.get(...);
     }
  }
}

次に、呼び出し元のコンポーネントまたはサービスで、次のように使用します

  ...
  export class MyService/*or MyComponent*/{
      constructor(private http: HttpClient){}
      doTheThing(){
          SomeNotInjectableService.doSomething(this.http)/*...subscribe()*/;
      }
  }
0
Bellash