英文:
How can I make parts of a reusable component's template optional in a Vue 3 app?
问题
Here's the translated content:
我一直在使用Vue 3、TypeScript和The Movie Database (TMDB) API开发一个单页面应用(SPA)。
我有一个电影卡片组件(MovieCard.vue
),我在至少两个不同的上下文中使用它,我希望它在不同的上下文中有些不同的行为。
第一个上下文是MoviesList.vue组件
第二个上下文是ActorDetails.vue组件,在这里我希望它显示时没有评分平均值的“气泡”。
目标是使电影卡片组件的这部分变为可选:
<span :title="`Score: ${movie.vote_average}`" class="score">{{ movie.vote_average}}</span>
我已经尝试了在ActorDetails组件中的“显而易见”的方法,但它不起作用(因为有作用域样式,我更喜欢保留它):
.score {
display: none !important;
}
有没有一种简单可靠的方法来实现这个目标?
英文:
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?
答案1
得分: 2
Here is the translated content:
有经验的人也许可以发布更好的答案,但我会尝试一下。
如果我正确理解了您的问题,您想在用户在“常规”电影列表上时才显示每部电影的评分,而在用户进入演员列表时隐藏它。
您可以将另一个属性添加到movieCard组件作为布尔值,并根据您是否希望卡片具有评分来传递true/false。
<span v-if="showRating" :title="`评分:${movie.vote_average}`" class="score d-flex">{{movie.vote_average}}</span>
在props中:
showRating: {
type: Boolean,
default: false,
},
然后在MoviesList组件中:
<MovieCard :showRating="true" :movie="movie" :genres="genres" />
英文:
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" />
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论