I have been working on an SPA with Vue 3, TypeScript and The Movie Database (TMDB) API.
I want to display the movie poster on the movie details page if there is a poster, otherwise I want to display a placeholder (generic-poster-big.png).
For this purpose, in srccomponentsMovieDetails.vue
, I have:
<template>
<div class="poster-container text-center text-sm-start my-3">
<img
:src="genericPoster"
:alt="movie?.title"
class="img-fluid shadow-sm"
/>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { useRoute } from "vue-router";
import axios from "axios";
import env from "../env";
import { Movie } from "../models/Movie";
export default defineComponent({
name: "MovieDetails",
data() {
return {
route: useRoute(),
genericPosterBig: require("../assets/generic-poster-big.png"),
movie: null as Movie | null
};
},
mounted() {
this.getMovieDetails();
},
methods: {
getMovieDetails() {
axios
.get(
`${env.api_url}/movie/${this.route.params.id}?api_key=${env.api_key}`
)
.then((response) => {
this.movie = response.data;
})
.catch((err) => console.log(err));
},
computed: {
genericPoster() {
return !this.movie?.poster_path
? this.genericPosterBig
: `https://image.tmdb.org/t/p/w500/${this.movie?.poster_path}`;
},
}
});
</script>
The problem
When there is a movie poster, before it loads, the generic poster appears, for a fraction of a second, yet visible.
Stackblitz
You can see a Stackblitz HERE.
Questions
- What am I doing wrong?
- What is the most reliable way to fix this problem?
3
Answers
Apparently, making sure that the movie is loaded before trying to render the poster at all, with
v-if
, fixed the issue:Something like the following should do the trick, added lines marked with
**********
This behaviour is expected given your solution, but I understand you want to improve this for UX reasons.
The problem is that your component is mounted before the data is available, so you need to set a logic on what to do while it’s loading
I see two solutions for this:
While it loads, use a loading spinner on your image while it’s loading, not the image placeholder (which should appear only once the poster details has been fetched). This is useful to avoid layout flickering: it will reserve the space required by the image.
useAsyncData()
composable (or for option api), so you are sure you already have all the movies data when creating the details page (so no flickering).