What is the best way to incorporate HttpClient into a static method or custom class?

Is it possible to utilize the angular HttpClient in a static method or class without being declared as a constructor parameter?

I made an attempt with the following code snippet:

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
  }
}

This was my experiment at manually injecting the client in a static service method. Unfortunately, it did not work as intended. I am interested in finding out if there is a way to accomplish this task, or how to properly inject the client in a regular method within a non-component class.

Answer №1

If you find yourself without an injector, don't worry - you can handle the 'injecting' process manually. However, I must caution against this approach. If you insist on using a static method instead of a proper service, make sure to provide all necessary components.

It is worth noting that any HTTP interceptor will be absent from the httpClient pipeline when taking this DIY route, as there is no mechanism in place for their resolution.

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

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

Alternatively, you can utilize your own custom injector (if you prefer not passing constructor arguments):

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));

Answer №2

There seems to be a slight discrepancy in why the method you attempted didn't function as expected (possibly due to a missing component during injector creation), but it does work when utilizing an 'injected' injector.

Upon examining the error-throwing source code, it indicates handlers for the request which appear to be null in your scenario. It's possible that Angular registers some internal handlers when HttpClient is provided in the 'traditional' manner, unlike the approach you've taken.

// Commence with an Observable.of() for the initial request and execute the handler (inclusive of all interceptors) within a concatMap(). This enables the handler to operate within an Observable chain, triggering interceptors to re-run upon each subscription (this also applies to retries re-running the handler, along with interceptors).
var /** @type {?} **/ events$ = rxjs_operator_concatMap.concatMap.call(rxjs_observable_of.of(req), function (req) { return _this.handler.handle(req); });

Solution:

app.module.ts

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

export let InjectorInstance: Injector;

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

Your static class/method

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(...)...
  }
}

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

Answer №3

Based on the answer provided by Andrew. To incorporate interceptors into the httpClient pipeline, you will need to include two modified classes from the angular repository http/src/interceptor.ts and 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);
    }
}

Remember that interceptors do not require the @Injectable decorator:

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

As Andrew mentioned:

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

Answer №4

Utilizing the need for passing a service or object as a parameter can greatly benefit your code. Not only does it aid in testing, but it also enhances the readability of your code. This approach allows for injecting any type of object required and ensures that it is done at the necessary point in the code. The responsibility of injecting the required object lies with the calling object.

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

In your calling component or service, you can implement it like this:

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Vue warning: Issue in rendering: "TypeError: Circular structure being converted to JSON"

After successfully creating a Single File Component in Vue without any compilation errors, I faced an issue when trying to view the component through its route link. Instead of the expected page, I encountered a stack trace printed in the Chrome browser us ...

The i18next module encounters compilation issues when used in a React application with Typescript

In my React-Typescript project, I recently set up i18next for multi-language support. Despite following the official documentation guidelines, I encountered compilation errors when running the app: Property 'changeLanguage' does not exist on type ...

Converting Venn diagram code from JavaScript <script> tags to Angular 2: A step-by-step guide

I am struggling to incorporate a Venn diagram into my Angular 2+ project. I followed the code sample provided at - http://jsfiddle.net/johnpham92/h04sknus/ To begin, I executed the following command - npm install venn.js Then I proceeded with impl ...

Exploring Typescript's Generic Unions

I'm facing an issue where I have an Array of generic objects and want to iterate over them, but TypeScript is not allowing me to do so. Below is a snippet of the code I am working with. Any ideas on how to solve this problem? type someGeneric<T> ...

Troubleshooting Docker-compose, Nx, Angular issue "Received no data from localhost."

Attempting to deploy an Angular app with Nx using Docker compose has encountered a problem. docker-compose.yml : services: client: build: context: . dockerfile: Dockerfile volumes: - .:/app - /app/node_modules ports: ...

Upgrade to Angular 12: TypeScript is now an essential requirement for the Angular Compiler

Recently, I made sure to update my project to the latest Angular version. After running "ng update", I received a confirmation that everything was already up to date, indicating that all required packages had been successfully updated in the last step of t ...

Which would be more effective: implementing Spring Security for role-based authorization, using Angular Route Guards, or combining both approaches?

I'm currently working on a project using Spring Boot for the backend and Angular for the front end. I'm wondering if it's best practice to implement both Spring Security role-based authentication and Angular route guards, or if just using Sp ...

An informative step-by-step approach to constructing Angular applications utilizing npm and TypeScript

When I first encountered Angular2, I was introduced to TypeScript, npm, and more for the very first time. I was amazed by their power, but I know I've only scratched the surface. While I can navigate through the "development mode," my ultimate goal i ...

What is the best way to test an oclif CLI tool that interacts with a Rest API

How can I efficiently test the TypeScript code I've written for an Oclif CLI that interacts with a Node.js and Express.js REST API? I'm currently using Mocha/Chai for testing, but I'm struggling with testing the specific command code from my ...

The parameter label is being detected as having an any type, as specified in the Binding element 'label'

Currently, I am referencing an example code snippet from react-hook-form. However, upon implementation, I encounter the following error: (parameter) label: any Binding element 'label' implicitly has an 'any' type.ts(7031) The example c ...

Error in Typescript: The property 'children' is not included in the type but is necessary in the 'CommonProps' type definition

Encountering this error for the first time, so please bear with me. While working on a project, I opened a file to make a change. However, instead of actually making any changes, I simply formatted the file using Prettier. Immediately after formatting, t ...

Upon clicking the save button, the FormArray does not trigger the display of any mat error

When I click on the save button outside of the form, I want to display a mat error. However, the error is not getting displayed. I have tried using this.form.markAsDirty() and this.form.markASTouched(), but none of them seem to work. <form [formGroup ...

What is the method for developing a Typescript-connected High-Order React Component with Redux?

I am looking to develop a React Higher-Order Component that can safeguard routes within my application from unauthorized users without an access token. I aim to use this HOC to wrap a Component like so in the parent component: <Route exact path ...

Error message: "Angular requires either importing or local installation"

For my ionic application development, I encountered an issue while trying to link pages together in the ionic creator. The error message on the .ts file is: typescript: src/pages/home/home.ts, line: 4 Individual declarations in merged declar ...

Creating Angular unit test modules

When it comes to creating unit test cases for an Angular app, the application functionality is typically divided into modules based on the requirements. In order to avoid the need for repeated imports in component files, the necessary components, modules, ...

Dependencies of generic types among function arguments

Exploring the implementation of binding in a game engine, I aim to incorporate a touch of typing. /** Engine external functions */ export type Message<TBody> = { } // This function returns the same unique object for the same `s` // An internal engi ...

Managing dependencies and automating setup processes can be tricky when incorporating Typescript into a

Query: How can I easily set up Typescript with Symfony without making extensive changes to Symphony's configuration files? Here are the key requirements for the solution: Typescript MVC Pattern should be set up in a private typescript directory: ...

Use TypeScript types to specify the types of props passed into a React component for better type safety and clarity

Struggling to extract the value passed to a prop in my react component, and use it as a type for other props within the same component. const TestPage = () => { return ( <Test tabs={[ { label: "test-label", value: " ...

Creating a Persistent Top Navigation Bar using Bootstrap and Angular

I am struggling to implement a fixed top navbar in Angular. The structure of my main app.component template is as follows: <page-header></page-header> <router-outlet></router-outlet> The bootstrap navbar is included within my ...

How can Observables be designed to exhibit both synchronous and asynchronous behavior?

From: Understanding the Contrasts Between Promises and Observables In contrast, a Promise consistently operates asynchronously, while an Observable can function in synchronous or asynchronous manners. This presents the opportunity to manipulate code in ...