I have been working on an SPA with Angular 16, TypeScript and The Movie Database (TMDB).
I run into a strange problem while working on a movies search feature.
In appservicesmovie-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 { MovieResponse, Movie } from '../models/Movie';
@Injectable({
providedIn: 'root'
})
export class MovieService {
constructor(private http: HttpClient) {}
public searchMovies(searchTerm: string): Observable<MovieResponse> {
return this.http.get<MovieResponse>(`${environment.apiUrl}/search/movie?api_key=${environment.apiKey}&query=${searchTerm}`);
}
}
I use the method above inside the TopBarComponent
like this:
import { Component } from '@angular/core';
import { MovieResponse, Movie } from '../../models/Movie';
import { MovieService } from '../../services/movie-service.service';
@Component({
selector: 'app-top-bar',
templateUrl: './top-bar.component.html',
styleUrls: ['./top-bar.component.scss']
})
export class TopBarComponent {
constructor(private movieService: MovieService) { }
public searchTerm: string = '';
public isSearch: boolean = false;
public timeOutInterval: number = 500;
public searchResultsResponse!: MovieResponse;
public searchResults: Movie[] | undefined = [];
public hideSearchResults(): void {
this.isSearch = false;
}
public debounceMovieSearch(): void {
setTimeout(() => this.doMovieSearch(), this.timeOutInterval);
}
public doMovieSearch() {
if (this.searchTerm && this.searchTerm.length > 2) {
this.isSearch = true;
this.movieService.searchMovies(this.searchTerm).subscribe((response) => {
this.searchResultsResponse = response;
this.searchResults = this.searchResultsResponse.results;
})
} else {
this.isSearch = false;
}
}
}
Search form:
<form class="search_form w-100 mx-auto mt-2 mt-md-0">
<div class="input-group">
<input type="search" name="search" [(ngModel)]="searchTerm" (input)="debounceMovieSearch()" placeholder="Search" autocomplete="off" class="form-control search-box">
<button class="btn btn-search" type="button">Search</button>
</div>
<div *ngIf="isSearch" (clickOutside)="hideSearchResults()" class="search-results shadow-sm">
<div *ngIf="searchResults && searchResults.length">
<a routerLink="/movie/{{ movie.id }}" *ngFor="let movie of searchResults">
<app-search-item [movie]="movie"></app-search-item>
</a>
</div>
<div *ngIf="!(searchResults && searchResults.length)">
<p class="m-0 p-2 text-center">No movies found for this search</p>
</div>
</div>
</form>
The routes in the appapp-routing.module.ts
:
const routes: Routes = [
{ path: '', component: HomePageComponent, data: { title: 'Now playing' } },
{ path: 'top-rated', component: TopMoviesComponent, data: { title: 'Top Rated' } },
{ path: 'movie/:id', component: MovieDetailsComponent, data: { title: '' } },
{ path: 'actor/:id', component: ActorDetailsComponent, data: { title: '' } },
{ path: '**', component: NotFoundComponent, data: { title: '' } },
];
The result looks like this:
The problem
Clicking a movie item in the searcg results list will navigate us to the movie details route (MovieDetailsComponent
), except when we already are on a movie details page.
Questions
- What am I doing wrong?
- What is the most reliable way to fix this issue?