Search code examples
javascriptangularrest

Angular 17 Http client injection


I am building a new app for testing with angular 17 trying to connect to a simple API rest. I am desperate to get this to work. I have asked chat GPT, reviewed stack's older posts and even download an old application, but with no success.

Here is my app config and the error:

app.module.ts

import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import {AppComponent} from "./app.component";
import {HttpClientModule} from "@angular/common/http";
import {ClienteService} from "./cliente.service";
import {ClienteComponent} from "./cliente/cliente.component";
import {AppRoutingModule} from "./app-routing.module";



@NgModule({
  declarations: [
    AppComponent,
    ClienteComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    ReactiveFormsModule,
    HttpClientModule,
    AppRoutingModule

  ],
  providers: [ClienteService],
  bootstrap: [AppComponent]
})
export class AppModule { }

app-routing.module.ts


import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import {ClienteComponent} from "./cliente/cliente.component";

const appRoutes: Routes = [
  { path: '', redirectTo: '/clientes', pathMatch: 'full' },
  { path: 'clientes', component: ClienteComponent}
];

@NgModule({
  imports: [RouterModule.forRoot(appRoutes)],
  exports: [RouterModule]
})
export class AppRoutingModule {

}

cliente.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class ClienteService {

  private clienteUrl: string;

  constructor(private http: HttpClient) {
    this.clienteUrl = 'http://localhost:8081/clientes';
  }
  getClientes(nombre?: string, telefono?: string, direccion?: string) {
    const params: any = { nombre, telefono, direccion };
    return this.http.get(this.clienteUrl, { params });
  }

cliente.component.ts

import {Component, OnInit} from '@angular/core';
import { CommonModule } from '@angular/common';
import {ClienteService} from "../cliente.service";

@Component({
  selector: 'app-cliente',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './cliente.component.html',
  styleUrl: './cliente.component.css'
})
export class ClienteComponent implements OnInit{

  clientes: any[] = [];

  constructor(private clienteService: ClienteService) {

  }

  ngOnInit(): void {
    this.clienteService.getClientes().subscribe((clientesResponse:any) => {
      console.log('Respuesta del servicio getAllClientes',clientesResponse);
      this.clientes=clientesResponse._embedded.clientes;


    });
  }

}

The problem is this:

ERROR NullInjectorError: R3InjectorError(Standalone[_AppComponent])[_ClienteService -> _ClienteService -> _HttpClient -> _HttpClient]: 
  NullInjectorError: No provider for _HttpClient!
    at NullInjector.get (core.mjs:5601:27)
    at R3Injector.get (core.mjs:6044:33)
    at R3Injector.get (core.mjs:6044:33)
    at injectInjectorOnly (core.mjs:911:40)
    at Module.ɵɵinject (core.mjs:917:42)
    at Object.ClienteService_Factory [as factory] (cliente.service.ts:7:28)
    at core.mjs:6164:43
    at runInInjectorProfilerContext (core.mjs:867:9)
    at R3Injector.hydrate (core.mjs:6163:17)
    at R3Injector.get (core.mjs:6033:33)
handleError @ core.mjs:11747

Solution

  • You are mixing Modules and Standalone Components. As of Angular 17 everything is standalone by default, unless you specify otherwise.

    You have 2 options.

    1. Don't use stand alone components, make your components part of modules, then the import arrays in the modules should work.
    2. Work without modules and make the app completely standalone. As intended from Angular 17

    If you started a project in angular 17, I suggest against option number one.

    For start, go to main.js and add provideHttpClient(withFetch()) to the providers array. Insted of importing HttpClientModule in your AppModule.

    bootstrapApplication(AppComponent, {
      providers: [
        provideHttpClient(withFetch()),
      ],
    });
    

    Further Explanation

    The new implementation for Angular 17 might seem obscure compared to the previous versions, so here's a bit more detail.

    Standalone Components vs. Traditional Modules

    Standalone Components:

    • Self-contained: Each component declares its own dependencies and configuration, making it easier to manage and understand.
    • No Need for NgModules: You don't have to create separate module files (NgModule), which simplifies your project structure.

    Traditional Modules:

    • Grouped Configuration: Dependencies are declared in NgModule files, which group together related components, directives, and pipes.
    • Imports and Declarations: You use imports and declarations arrays in NgModule to manage dependencies and components.

    Implementing Standalone Components

    • Creating Standalone Components: When generating a new component, you can specify it as standalone.
    ng generate component my-component --standalone
    
    • Bootstrap Configuration: Configure your application to use standalone components directly in the main.ts file.
    import { bootstrapApplication } from '@angular/platform-browser';
    import { AppComponent } from './app/app.component';
    import { provideHttpClient, withFetch } from '@angular/common/http';
    
    bootstrapApplication(AppComponent, {
      providers: [
        provideHttpClient(withFetch()), // Configure the HttpClient here
        // Add the rest of the providers here
      ],
    });
    

    Benefits of the Standalone Approach

    • Simplified Dependency Management: Standalone components declare their own dependencies, making it easier to manage and understand the required imports and providers.
    • Reduced Boilerplate: You no longer need to create and maintain NgModule files, which reduces the amount of boilerplate code in your application.
    • Efficiency: Standalone components can lead to better performance by reducing the overhead associated with NgModule resolution.
    • Flexibility: Standalone components are more flexible for use in different parts of your application or in other projects.
    • Future-Proofing: Angular is moving towards this modular, standalone approach. Adopting it now can make future updates and migrations smoother.