我一直在使用 Angular 16、TypeScript 和电影数据库 (TMDB) 开发 SPA。
我在开发电影搜索功能时遇到了一个奇怪的问题。
在
app\services\movie-service.service.ts
我有:
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}`);
}
}
我在
TopBarComponent
中使用上面的方法,如下所示:
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;
}
}
}
搜索表格:
<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>
app\app-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: '' } },
];
结果如下所示:
单击搜索结果列表中的电影项目将导航至电影详细信息路线 (
MovieDetailsComponent
),除了当我们已经位于电影详细信息页面时。
有一个 Stackblitz 包含我迄今为止拥有的所有代码。
你几乎是正确的,你缺少的是在
paramMap
中使用snapshot
而不是movie-details.component
,这样你就可以对电影id
的变化做出反应。
您需要在函数
getMovieDetails
上解决此更改,如下所示:
getMovieDetails(): void {
this.activatedRoute.paramMap.pipe(
map(params => params.get('id')), // retrieve id from route
switchMap((id: string | undefined) => {
if (id) return this.movieService.getMovieDetails(Number(id));
return of(undefined); // if there is no id, then return undefined
})
).subscribe((response: Movie | undefined) => {
if (response) { // if movie exists then do something...
this.movie = response;
// ...
}
})
}
当您从
movie/1
导航到 movie/2
时,不会重新创建 MovieDetailsComponent
。因为您不监听 id 更改,所以组件不知道电影 id 已更改,并且需要更新视图。所以监听路由参数变化:
ngOnInit() {
this.activatedRoute.paramMap.subscribe(paramMap => {
const movie_id = Number(paramMap.get('id'));
this.getMovieDetails(movie_id);
});
}