我一直在使用 Angular 16、TypeScript 和电影数据库 (TMDB) 开发 SPA。
我制作了一个按类型显示电影的组件:
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 MoviesByGenreComponent {
public movieResponse!: MovieResponse;
public movies: Movie[] | undefined = [];
public genreResponse!: GenreResponse;
public genres: Genre[] | undefined = [];
public genreName: string | undefined = '';
constructor(
private activatedRoute: ActivatedRoute,
private movieService: MovieService
) { }
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;
}
}
});
// Get movies by genre id
this.movieService.getMoviesByGenre(genre_id).subscribe((response) => {
this.movieResponse = response;
this.movies = this.movieResponse.results;
})
}
ngOnInit() {
this.getMoviesByGenre();
}
}
在上述组件使用的服务中,我有:
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';
import { GenreResponse } from '../models/Genre';
import { TrailerResponse } from '../models/Trailer';
@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}`);
}
}
在路由模块中,我有:
const routes: Routes = [
{
path: '',
component: HomePageComponent,
data: { title: 'Now playing', animation: 'isRight' },
},
{
path: 'by-genre/:id',
component: MoviesByGenreComponent,
data: { title: '', animation: 'isLeft' },
},
{
path: 'movie/:id',
component: MovieDetailsComponent,
data: { title: '', animation: 'isLeft' },
},
{
path: 'actor/:id',
component: ActorDetailsComponent,
data: { title: '', animation: 'isRight' },
},
{
path: '**',
component: NotFoundComponent,
data: { title: '', animation: 'isRight' },
},
];
从路由模块中,除非
title
对象中的 data
属性为空,否则我会在 app\app.component.html
中显示页面标题:
<div class="container">
<h1 *ngIf="title.length" class="page-title text-success mt-2 mb-3">{{ title }}</h1>
<router-outlet></router-outlet>
</div>
对于
MoviesByGenreComponent
,我想将动态获取的流派名称(变量genreName
)从组件发送到路由器,并将genreName
分配给title
属性。
有一个 stackblitz 包含我迄今为止拥有的所有代码。
您可以将帖子标题内容存储在服务上,并使用函数
getFullTitle
始终获取最新值,此方法的唯一警告是您需要确保每次组件被销毁时将服务标题设置为空字符串!
流派.com.ts 制作的电影
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 MoviesByGenreComponent {
public movieResponse!: MovieResponse;
public movies: Movie[] | undefined = [];
public genreResponse!: GenreResponse;
public genres: Genre[] | undefined = [];
public genreName: string | undefined = '';
constructor(
private activatedRoute: ActivatedRoute,
private movieService: MovieService
) {}
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.postTitle = ` - ${this.genreName}`;
}
}
});
// Get movies by genre id
this.movieService.getMoviesByGenre(genre_id).subscribe((response) => {
this.movieResponse = response;
this.movies = this.movieResponse.results;
});
}
ngOnInit() {
this.movieService.postTitle = '';
this.getMoviesByGenre();
}
}
app.com.html
<app-top-bar></app-top-bar>
<div class="container" [@routeAnimations]="prepareRoute(outlet)">
<h1
*ngIf="getFullTitle() as finalTitle"
class="page-title text-success mt-2 mb-3"
>
{{ finalTitle }}
</h1>
<router-outlet #outlet="outlet"></router-outlet>
</div>
<app-footer class="mt-auto"></app-footer>
app.com.ts
import { Component } from '@angular/core';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { RouterOutlet } from '@angular/router';
import { slider } from './route-animations';
import { MovieService } from './services/movie-service.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
animations: [slider],
})
export class AppComponent {
public title: String = 'Movies';
constructor(
private route: ActivatedRoute,
private router: Router,
private movieService: MovieService
) {
this.router.events.subscribe((event) => {
if (event instanceof NavigationEnd && this.route.root.firstChild) {
this.title = this.route.root.firstChild.snapshot.data['title'];
}
});
}
public getFullTitle() {
return this.title + this.movieService.postTitle;
}
public prepareRoute(outlet: RouterOutlet) {
return (
outlet &&
outlet.activatedRouteData &&
outlet.activatedRouteData['animation']
);
}
}