I have been working on an SPA with Angular 16, TypeScript and The Movie Database (TMDB).
I run into a strange issue while working on a list movies by genre feature.
In app\services\movie-service.service.ts
I have:
import { environment } from '../../environments/environment';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { GenreResponse } from '../models/Genre';
@Injectable({
providedIn: 'root'
})
export class MovieService {
constructor(private http: HttpClient) { }
public getAllMovieGenres(): Observable<GenreResponse> {
return this.http.get<GenreResponse>(`${environment.apiUrl}/genre/movie/list?api_key=${environment.apiKey}`);
}
public getMoviesByGenre(id: Number): Observable<MovieResponse> {
return this.http.get<MovieResponse>(`${environment.apiUrl}/discover/movie?api_key=${environment.apiKey}&with_genres=${id}`);
}
}
I use the above methods in the MoviesByGenre
component:
import { Component } from '@angular/core';
import { GenreResponse, Genre } from '../../models/Genre';
import { MovieResponse, Movie } from '../../models/Movie';
import { MovieService } from '../../services/movie-service.service';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-movies-by-genre',
templateUrl: './movies-by-genre.component.html',
styleUrls: ['./movies-by-genre.component.scss']
})
export class MoviesByGenre {
constructor(
private activatedRoute: ActivatedRoute,
private movieService: MovieService
) { }
public genreName: string | undefined = '';
public movieResponse!: MovieResponse;
public movies: Movie[] | undefined = [];
public genreResponse!: GenreResponse;
public genres: Genre[] | undefined = [];
public getMoviesByGenre(): void {
// Get genre id (from URL parameter)
const genre_id = Number(this.activatedRoute.snapshot.paramMap.get('id'));
// Get genre name from genres array
this.movieService.getAllMovieGenres().subscribe((response) => {
this.genreResponse = response;
this.genres = this.genreResponse.genres;
if (this.genres && this.genres.length) {
let currentGenre = this.genres.find(genre => genre.id === genre_id);
if (currentGenre) {
this.genreName = currentGenre.name || '';
this.movieService.defaultTitle = this.genreName;
}
}
});
// Get movies by genre id
this.movieService.getMoviesByGenre(genre_id).subscribe((response) => {
this.movieResponse = response;
this.movies = this.movieResponse.results;
})
}
ngOnInit() {
this.getMoviesByGenre();
}
}
Whenever I display movies of a certain genre and try to navigate to another genre, for instance, from localhost:4200/by-genre/12
to localhost:4200/by-genre/18
, the nre data is not loaded (even though the URL does change).
In other words using this to get and use the genre_id
fails:
const genre_id = Number(this.activatedRoute.snapshot.paramMap.get('id'));
When you switch between variations of the same route ( the query params alone change ) the component does not reload, so to handle this scenario, angular router provides an observable which will notify when the route params changes, this can be used to trigger a rerun of genre fetch API.
Its always a good idea to unsubscribe all subscriptions, during destroy of component
public getMoviesByGenre(genre_id: any): void {
// Get genre id (from URL parameter)
// Get genre name from genres array
this.subscription.add(
this.movieService.getAllMovieGenres().subscribe((response) => {
this.genreResponse = response;
this.genres = this.genreResponse.genres;
if (this.genres && this.genres.length) {
let currentGenre = this.genres.find(genre => genre.id === genre_id);
if (currentGenre) {
this.genreName = currentGenre.name || '';
this.movieService.defaultTitle = this.genreName;
}
}
})
);
// Get movies by genre id
this.subscription.add(this.movieService.getMoviesByGenre(genre_id).subscribe((response) => {
this.movieResponse = response;
this.movies = this.movieResponse.results;
}));
}
ngOnInit() {
this.subscription.add(
this.activatedRoute.params.subscribe((params: Params) => {
const id = params?.id;
this.getMoviesByGenre(id);
})
);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}