Я работаю над SPA с API Vue 3, TypeScript и The Movie Database (TMDB).
У меня есть компонент карты фильма (MovieCard.vue), который я использую (как минимум) в двух разных контекстах, и я хочу, чтобы он вел себя немного по-разному в зависимости от контекста.
<template>
<div class = "movie card" @click = "showDetails(movie.id)">
<div class = "thumbnail">
<img
:src = "movieCardImage"
:alt = "movie.title"
class = "img-fluid"
/>
</div>
<div class = "card-content">
<h2 class = "card-title">{{ movie.title }}</h2>
<p class = "card-desc">{{ movie.overview }}</p>
<span :title = "`Score: ${movie.vote_average}`" class = "score">{{ movie.vote_average}}</span>
</div>
<div class = "card-footer">
<p class = "m-0 release">
Release date: {{ dateTime(movie.release_date) }}
</p>
<p v-if = "movieGenres" class = "m-0 pt-1">
<span class = "genre" v-for = "genre in movieGenres" :key = "genre.id">
{{ genre.name }}
</span>
</p>
</div>
</div>
</template>
<script lang = "ts">
import { defineComponent } from "vue";
import moment from "moment";
export default defineComponent({
name: "MovieCard",
props: {
movie: {
type: Object,
required: true,
},
genres: {
type: Object,
required: false,
},
},
data: () => ({
genericCardImage: require("../assets/generic-card-image.png"),
}),
methods: {
dateTime(value: any) {
return moment(value).format("MMMM DD, YYYY");
},
showDetails(movie_id: any) {
let movie_url = `movie/${movie_id}`;
window.open(movie_url, "_self");
},
},
computed: {
movieCardImage() {
return !this.movie?.backdrop_path
? this.genericCardImage
: `https://image.tmdb.org/t/p/w500/${this.movie?.backdrop_path}`;
},
movieGenres() {
if (this.genres) {
let genres = this.genres;
return this.movie?.genre_ids
.filter((genre_id: number) => genre_id in genres)
.map((genre_id: number) => genres[genre_id])
.filter((genre: { name: string }) => genre.name.length > 0);
} else {
return [];
}
},
},
});
</script>
Первый контекст — это компонент MoviesList.vue.
<template>
<div class = "row list">
<div
v-for = "movie in movies"
:key = "movie.id"
class = "col-xs-12 col-sm-6 col-lg-4 col-xl-3"
>
<MovieCard :movie = "movie" :genres = "genres" />
</div>
</div>
</template>
<script lang = "ts">
import { defineComponent } from "vue";
import axios from "axios";
import env from "../env";
import MovieCard from "./MovieCard.vue";
export default defineComponent({
name: "MoviesList",
components: { MovieCard },
props: {
listType: {
type: String,
required: true,
},
},
data: () => ({
searchTerm: "",
movies: [],
genres: [],
}),
mounted() {
this.listMovies();
this.getGenres();
},
methods: {
listMovies() {
axios
.get(
`${env.api_url}/movie/${this.$props.listType}?api_key=${env.api_key}`
)
.then((response) => {
this.movies = response.data.results;
})
.catch((err) => console.info(err));
},
getGenres() {
axios
.get(`${env.api_url}/genre/movie/list?api_key=${env.api_key}`)
.then((response) => {
this.genres = response.data.genres;
})
.catch((err) => console.info(err));
},
},
});
</script>
Результат:
Второй контекст — это компонент ActorDetails.vue, где я хочу, чтобы он отображался без среднего «пузыря» голосов:
<div v-if = "knownFor" class = "movies">
<h2 class = "section-title">Known For</h2>
<div class = "row list">
<div
v-for = "item in knownFor"
:key = "item.id"
class = "col-xs-12 col-sm-6 col-lg-4 col-xl-3"
>
<MovieCard :movie = "item" />
</div>
</div>
</div>
Желаемый результат:
Вы можете увидеть Stackblitz ЗДЕСЬ.
Я хочу сделать эту часть компонента карточки фильма необязательной:
<span :title = "`Score: ${movie.vote_average}`" class = "score">{{ movie.vote_average}}</span>
Я попробовал «очевидный» способ в компоненте ActorDetails, но он не работает (из-за стилей с ограниченной областью действия, которые я предпочитаю сохранять):
.score {
display: none !important;
}



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Кто-то более опытный может опубликовать лучший ответ, но я попробую. Если я правильно понял вашу проблему, вы хотите показывать рейтинг каждого фильма только тогда, когда пользователь находится в «общем» списке фильмов, и скрывать его, когда пользователь находится в списке актеров. Вы можете добавить еще один реквизит к компоненту movieCard как логическое значение и передать его как true/false в зависимости от того, хотите ли вы, чтобы у карты был рейтинг или нет.
<span v-if = "showRating" :title = "`Score: ${movie.vote_average}`" class = "score d-flex">{{movie.vote_average}}</span>
и на реквизите
showRating: {
type: Boolean,
default: false,
},
а затем в компоненте MoviesList:
<MovieCard :showRating = "true" :movie = "movie" :genres = "genres" />
:showRating = "true" можно сократить до showRating для логической опоры, но в остальном все так просто.
Очень простое и надежное решение. Именно то, что я искал. :)