Failure to Execute Angular HttpClient Request

I'm facing an issue with firing the HttpClient request. It seems like there might be a problem with importing or providing, but I can't pinpoint where it is exactly. The API works fine, but the call never goes through.

Here are the environment/versions details:

  • Angular: 15.1.2 Angular
  • CLI: 15.1.3
  • Node: 16.14.2
  • Package Manager: npm 8.5.0
  • OS: win32 x64

The structure is such that I have a main landing page with various widgets that offer a quick glimpse into the targeted content. Each widget has its own component. These components subscribe to Observables provided by services, which provide data for the widgets and are injected at the root.

app.module.ts

const pipes = [
  SignUpFormTextPipe,
  StageFormatTextPipe,
  StageTypeTextPipe,
  TeamSizeTextPipe,
  TournamentStatusTextPipe,
];

const providers = [HttpClientModule];

@NgModule({
  declarations: [...components, ...pipes],
  imports: [
    BrowserModule,
    FormsModule,
    ReactiveFormsModule,
    AppRoutingModule,
    HttpClientModule,
  ],
  providers: [...pipes, ...providers],
  bootstrap: [AppComponent],
})

The service

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Tournament } from '../models/tournament/tournament';
import { BehaviorSubject, Observable, take, tap } from 'rxjs';
import { TournamentsResponse } from '../models/api-responses/tournament/tournaments-response';

@Injectable({
  providedIn: 'root',
})
export class TournamentService {
  private baseUrl: 'http://localhost:5077/api/tournamens';

  initialTournaments: Tournament[] = [];
  private tournaments = new BehaviorSubject<Tournament[]>(
    this.initialTournaments
  );

  public tournaments$ = this.tournaments.asObservable();

  constructor(private httpClient: HttpClient) {}

  public getTournaments(): Observable<TournamentsResponse> {
    console.log('test 123'); // this works

    return this.httpClient.get<TournamentsResponse>(this.baseUrl).pipe(
      tap((resp) => {
        console.log(resp); // this doesn't work

        this.tournaments.next(resp.tournaments);

        return resp;
      }),
      take(1)
    );
  }
}

One of the components subscribing to it:

import { Component, OnInit } from '@angular/core';
import { Observable, Subject, takeUntil } from 'rxjs';
import { Tournament } from 'src/app/models/tournament/tournament';
import { NotificationService } from 'src/app/services/common/notification.service';
import { TournamentService } from 'src/app/services/tournament.service';

@Component({
  selector: 'app-upcoming-tourneys',
  templateUrl: './upcoming-tourneys.component.html',
  styleUrls: ['./upcoming-tourneys.component.css'],
})
export class UpcomingTourneysComponent implements OnInit {
  private destroy$: Subject<void> = new Subject<void>();

  upcomingTournaments$: Observable<Tournament[]> =
    this.tournamentService.tournaments$;

  constructor(
    private tournamentService: TournamentService,
    private notificationService: NotificationService
  ) {}

  ngOnInit() {
    console.log(this.tournamentService); // this gets fired and logged properly

    this.tournamentService
      .getTournaments()
      .pipe(takeUntil(this.destroy$))
      .subscribe({
        next: (resp) => {
          console.log(resp); // this doesn't fire
        },
        error: (resp) => {
          this.notificationService.showError(resp);
        },
      });
  }
}

and the related components HTML:

test if component is rendered
<div class="container-fluid" *ngIf="upcomingTournaments$ | async as upcomingTournaments">
  <div class="card">
    <div class="card-header" routerLink="/tournaments">
      Upcoming tournaments
    </div>
    <div class="card-body">
       <div *ngFor="let tourney of upcomingTournaments">
         ... content...
       </div>
    </div>
  </div>
</div>

This seems to be quite a puzzling error since I have the same setup working in another app. Some initial suggestions indicate that it could be an import error, but neither intelisense nor linter hints at it.

*** UPDATE 1 *** I modified the service as per @flo's suggestion to use lastValueFrom(...). Unfortunately, the HTTP call still doesn't go through. :(

@Injectable({ providedIn: 'root' })
export class TournamentService {
  private baseUrl: 'http://localhost:5077/api/tournamens';

  initialTournaments: Tournament[] = [];
  private tournamentsSubject = new BehaviorSubject<Tournament[]>(
    this.initialTournaments
  );

  public tournaments$ = this.tournamentsSubject.asObservable();

  constructor(private httpClient: HttpClient) {}

  public getTournaments() {
    const result = lastValueFrom(
      this.httpClient.get<TournamentsResponse>(this.baseUrl)
    ).then((resp) => {
      this.tournamentsSubject.next(resp.tournaments);
    });
  }
}

In the component typescript file:

export class UpcomingTourneysComponent implements OnInit {
  upcomingTournaments$: Observable<Tournament[]> =
    this.tournamentService.tournaments$;

  constructor(private tournamentService: TournamentService) {}

  ngOnInit() {
    this.tournamentService.getTournaments();
  }
}

In the component template:

test if component is rendered
{{ upcomingTournaments$ | async }}
<div
  class="container-fluid"
  *ngIf="upcomingTournaments$ | async as upcomingTournaments"
>
....

*** UPDATE 2 *** As the previous attempts didn't work, I removed all observables and updated the service call. Still no luck with the API call being fired :*

The service:

  public getTournaments(): Observable<TournamentsResponse> {
    return this.httpClient.get<TournamentsResponse>(this.baseUrl);
  }

Component:

  ngOnInit() {
    const response = this.tournamentService.getTournaments().subscribe({
      next: (resp) => {
        console.log({ what: 'next call within service subscribe', resp }); // logs an ERROR TypeError: Cannot read properties of undefined (reading 'toLowerCase')
      },
    });
    console.log({ what: 'upcomingTourneysInit', response }); // here Response is SafeSubscriber
  }

Answer №1

You need to make sure you properly assign a value to your baseUrl in the service. The current code for the service is:

private baseUrl: 'http://localhost:5077/api/tournamens';

This is incorrect as it is not assigning the actual API url, but rather marking it as a type.

The correct way to declare this would be:

private baseUrl: string = 'http://localhost:5077/api/tournamens';

In addition, consider using Observables in your service and subscribing in your template with the async pipe to simplify handling subscriptions.

Answer №2

The issue lies within the tap function in your service. Make sure to refer to the documentation for clarification:

Tapping into the Observable The getHeros() method accesses the flow of observable values and displays a message, using the log() method, in the message area located at the bottom of the page.

The RxJS tap() operator facilitates this process by examining the observable values, performing actions on those values, and then passing them along. The tap() callback does not directly interact with the values themselves.

Keep in mind that Tap does not have direct access to the underlying data

Documentation Link

To fetch data from the http, you can utilize the function toPrimose (although it is deprecated - consider switching to lastValueFrom).

This is the recommended approach:

 public getTournaments(): Observable<TournamentsResponse> {
    console.log('test 123'); // this will execute
    const result = lastValueFrom(this.httpClient.get<TournamentsResponse>(this.baseUrl));

    this.tournaments.next(result.tournaments);      
);

Subsequently, make sure to subscribe to the tournaments$ in your application.

export class UpcomingTourneysComponent implements OnInit, OnDestroy {
  upcomingTournaments!: any;
  tournamentSub!: Subscription;

  constructor(private tournamentService: TournamentService) {}

  ngOnInit() {
    this.tournamentSub = this.tournamentService.tournaments$.subscribe(data => {
      upcomingTournaments = data;
    })
    this.tournamentService.getTournaments();
  }

  ngOnDestroy() {
    this.tournamentSub.unsubscribe()
  }
}

Following these steps will yield the desired outcome.

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

Using PersistedModel.create(Array) will generate an object containing properties that are numbered sequentially

Here is a piece of code that makes a call to a Loopback API method with an array of values. The input data is correct, no errors are thrown by the API, and the subscribe block executes as expected. const newStudentGroups = selectedStudentI ...

What is the best way to manage a custom child event that is triggered using this.$emit in a parent component, specifically within the <script> section of the .vue file?

In our project, we're utilizing vue and typescript, which means that our .vue files are structured very similarly to the layout outlined in this blogpost. One of our child components is emitting a custom event called changeType. I'd like to trig ...

Encountered an unhandled exception: Module 'source-map' not found while attempting to run Angular on a Raspberry Pi

I am currently developing an Angular application to run on a Raspberry Pi. However, I encountered the following error when trying to start the application: An unhandled exception occurred: Cannot find module 'source-map' Require stack: - /home/pi ...

The Angular application is unable to find any matching routes while operating on a secure HTTPS

I am facing an issue with my Angular application created using the Radzen IDE and hosted on IIS. There is an anchor tag in the application that links to a .Net API for downloading files. The href of the anchor tag is structured like this: https://[Angular ...

TypeScript: implementing function overloading in an interface by extending another interface

I'm currently developing a Capacitor plugin and I'm in the process of defining possible event listeners for it. Previously, all the possible event listeners were included in one large interface within the same file: export interface Plugin { ...

Using TypeScript to pass the text field ID to a function for clearing the text field with a button

I am looking for a way to improve the functionality of my web page featuring several buttons that will clear different text boxes on the same line. Currently, I am using separate functions for each button, but my goal is to streamline this process by utili ...

Validating a single field name with various DTO types based on conditions in a NestJS application

There is a field named postData in EmailTypeDto, but it has different types based on conditions. It may be confusing to explain in words, but the code makes it clear. export class EmailTypeDto { @IsEnum(EmailType) public type: EmailType; @ValidateIf ...

The slice() method in arrays provides a reference to the elements rather than copying

In my file, I am exporting an object in the following manner: export const LINECHART2_DATA = { series: [{ data: [], name: 'HR', }, { etc... }] } The way I import it is like this: import { LINECHART2_DAT ...

During the test, an unexpected error occurred: Configuration file error! Module 'karma-remap-istanbul' not found

Whenever I run ng test, I keep encountering the following error message - what could be causing this issue? An unhandled exception occurred: Error in configuration file! Error: Module 'karma-remap-istanbul' cannot be found Below is the content ...

Perform a delayed evaluation of the length of the @Input() array

In my Component, I am utilizing an @Input() ids: string[] = []; variable to check if the length equals 1 in the DOM. <mat-expansion-panel *ngFor="let id of ids" [expanded]="ids.length === 1"> ... </mat-expansion-panel> However, when I append ...

A Guide to Implementing Schema.virtual in TypeScript

After switching from using schema.virtual in JavaScript to TypeScript, I encountered an error when trying to use it with TypeScript. Below is my code: UserSchema.virtual('fullname').get(function () { return `${this.firstName} ${this.lastName}` ...

Unveiling the Power of Angular 4's HttpClient for Eff

I have encountered an issue with my API, where it returns ISO date that I need to convert into a JavaScript date. Despite using the HTTPClient module for automatic mapping, the data received is not being transformed as expected. While I am aware that it c ...

Finding the final day of a specific year using the moment library

When it comes to determining the last day of a year, hard-coding the date as December 31st seems like a simple solution. While there are various methods using date, js, and jquery, I am tasked with working on an Angular project which requires me to use mom ...

The `@ViewChild` reference cannot be found

My main challenge is toggling a @ViewChild element using an *ngIf, followed by invoking a native event. This snippet shows my HTML element, tagged with #audioPlayer for extracting it through @ViewChild. <audio #audioPlayer *ngIf="convers ...

What is the best method to terminate an Electron application using TypeScript?

I have been searching for the proper method to close an Electron app. My app uses React and TypeScript. After coming across this helpful post, I discovered a working solution: const remote = require('electron').remote; let w = remote.getCurrentW ...

Enhance the API response for Angular service purposes

I am currently working on modifying the response returned by an API request. At the moment, I receive the response as: [ { name: "Afghanistan" }, { name: "Åland Islands" } ] My goal is to adjust it to: [ { name: "A ...

Error: Android package not found when building a NativeScript app with TypeScript

I encountered the following error message: main-page.ts(15,26): error TS2304: Cannot find name 'android'. This error occurred after setting up a new NativeScript project using TypeScript. tns create demo --template typescript I then added the ...

What is the best way to implement an Angular Guard that utilizes an API service for validation and redirects in case of failure?

Hello there! I am currently working on an Angular 7 application that deals with time cards. One of the main features I have implemented is a CanActivate Guard for controlling access to certain components. The CanActivate code utilizes Observables to decid ...

Steps for integrating a universal loader in Angular

My implementation of a global loader is as follows: In the CoreModule: router.events.pipe( filter(x => x instanceof NavigationStart) ).subscribe(() => loaderService.show()); router.events.pipe( filter(x => x instanceof NavigationEnd || x in ...

Utilizing the URL path name for data retrieval in Next.js 14 - A step-by-step guide

I'm currently developing a blog using AWS Amplify Gen 2 and GraphQL for a Next.js 14 project with TypeScript. As part of my application, I need to fetch specific data based on the URL path name. Here's how I've approached it: My approach in ...