I have been working on an SPA with Vue 3, TypeScript and The Movie Database (TMDB) API.
I have a movie card component (MovieCard.vue) that I use in (at least) 2 diffrent contexts and I want it to behave slightly difrentlly from one context to another.
<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>
The first context is the MoviesList.vue component
<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.log(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.log(err));
},
},
});
</script>
The result:
The second context is the ActorDetails.vue component, where I want it displayed without the votes average "bubble":
<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>
The desired result:
Stackblitz
You can see a Stackblitz HERE.
The goal
I want to make this part of the movie card component optional:
<span :title="`Score: ${movie.vote_average}`" class="score">{{ movie.vote_average}}</span>
I have tried the "obvious" way, in the ActorDetails component, but it does not work (due to scoped styles, which I prefer to keep):
.score {
display: none !important;
}
What is an easy and reliable way to achieve this goal?
>Solution :
Someone more experienced can maybe post a better answer but i’ll give it a go.
If i understood your issue correctly is you want to show the rating of each movie only when user is on "general" movies list and hide it when user is on the actor’s one.
You can add another prop to the movieCard component as a Boolean and pass it as true/false depending if you want the card to have a rating or not.
<span v-if="showRating" :title="`Score: ${movie.vote_average}`" class="score d-flex">{{movie.vote_average}}</span>
and on props
showRating: {
type: Boolean,
default: false,
},
and then on MoviesList component:
<MovieCard :showRating="true" :movie="movie" :genres="genres" />

