【问题标题】:How to inject HttpClient in static method or custom class?如何在静态方法或自定义类中注入 HttpClient?
【发布时间】:2018-09-05 13:40:41
【问题描述】:

我想在静态方法或类中使用角度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
  }
}

这是在静态服务方法中手动注入客户端的尝试。不工作。我很好奇如何做到这一点,或者如何在普通方法中注入客户端,但在一个不是组件的类中。

【问题讨论】:

  • 你总是可以避免使用静态方法...
  • 仍然,问题是如何在普通方法中注入 HttpClient,但使用注入器而不是构造函数。上述解决方案可行吗?
  • 你还需要在静态方法中注入Injector :)
  • 我找不到解决方案。请帮帮我..

标签: angular typescript inject


【解决方案1】:

我不确定为什么它不能按照您尝试的方式工作(可能是在您创建注入器时缺少某些东西),但如果您使用“注入”注入器,它就可以工作

如果您查看引发错误的源代码,您会发现它提到了请求的处理程序,在您的示例中这似乎为空。当 HttpClient 以“传统”方式提供时,也许 Angular 会注册一些内部处理程序,但不是您这样做的方式

// Start with an Observable.of() the initial request, and run the handler (which
// includes all interceptors) inside a concatMap(). This way, the handler runs
// inside an Observable chain, which causes interceptors to be re-run on every
// subscription (this also makes retries re-run the handler, including interceptors).

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

【讨论】:

  • 这是唯一的解决方案吗?如果我想将其编写为独立库并避免在任何组件/服务中声明任何内容怎么办?这只是一个例子。您的解决方案让我非常满意,但也许还有其他选择。
  • @elzoy 抱歉,我不太确定。也许将 httpClient 实例传递给您的静态方法作为解决方法?
  • 这是给 httpclient 的。如果我们在这个自定义类中包含大量可注入服务怎么办。
【解决方案2】:

如果您没有注射器,您也可以跳过注射器。这意味着自己进行“注射”。我不建议这样做。如果您真的想使用静态方法(支持适当的服务),请传递所有需要的东西。

我不确定这是否还不是很明显,但是这个 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));

【讨论】:

    【解决方案3】:

    基于安德鲁的回答。 如果您想在此 httpClient 管道中使用拦截器,请从 angular repo http/src/interceptor.ts 和 http/src/module.ts 添加两个重新定义的类:

    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] }
     ],
    });
    

    【讨论】:

      【解决方案4】:

      将需要的服务/对象作为参数传递会很有帮助。此外,它还有助于测试和代码“可读性”。以下解决方案适用于您尝试注入的任何类型的对象。而且,至少,您可以在需要的地方/时间注入它。调用对象负责注入需要的对象。

      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()*/;
            }
        }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-21
        • 2016-02-15
        • 1970-01-01
        • 1970-01-01
        • 2020-02-27
        相关资源
        最近更新 更多