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.
https://i.sstatic.net/AtEC4.png https://i.sstatic.net/QvBKR.png
*** 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
}