英文:
What causes the slice is not a function error in this Vue 3 and TypeScript app?
问题
Here's the translated code part:
我一直在使用**Vue 3**、**TypeScript** 和 **The Movie Database (TMDB) API** 来开发单页面应用。
在 `src\components\MovieDetails.vue` 中有以下代码:
	<template>
	  <div class="row">
		<div class="col-sm-4 col-md-3">
		  <div class="poster-container text-center text-sm-start my-3">
			<img v-if="movie"
			  :src="genericPoster"
			  :alt="movie?.title"
			  class="img-fluid shadow-sm"
			/>
		  </div>
		</div>
		<div class="col-sm-8 col-md-9">
		  <h1 class="movie-title mt-4">{{ movie?.original_title }}</h1>
		  <p v-if="movie?.genres.length">
			<strong class="pe-1">Genres:</strong>
			<span
			  class="badge bg-secondary"
			  v-for="genre in movie?.genres"
			  :key="genre.id"
			  >{{ genre.name }}</span
			>
		  </p>
		  <p v-if="movie?.vote_average" class="user-score">
			<strong>User Score:</strong>
			<span class="score" :class="movieQuality">{{ Number(movie?.vote_average).toFixed(2) }}</span>
		  </p>
		  <div v-if="movie?.overview">
			<h2 class="section-title">Overview</h2>
			<p>{{ movie.overview }}</p>
		  </div>
		  <div v-if="movieTrailers.length" class="mb-3">
			<h2 class="section-title">Trailers</h2>
			<TrailerCarousel :movieTrailers="movieTrailers" />
		  </div>
		  <div v-if="movieCast.length" class="cast-container">
			<h2 class="section-title">Cast</h2>
			<div class="row">
			  <div
				v-for="actor in movieCast.slice(0, 12)"
				:key="actor['id']"
				class="col-sm-3"
			  >
				<ActorCard :actor="actor" />
			  </div>
			</div>
		  </div>
		</div>
	  </div>
	</template>
	<script lang="ts">
	import { defineComponent } from "vue";
	import { useRoute } from "vue-router";
	import env from "../env";
	import { Movie } from "@/models/Movie";
	import { Trailer } from "@/models/Trailer";
	import { MovieCast } from "@/models/MovieCast";
	import TrailerCarousel from "./TrailerCarousel.vue";
	import ActorCard from "./ActorCard.vue";
	export default defineComponent({
	  name: "MovieDetails",
	  components: {
		TrailerCarousel,
		ActorCard,
	  },
	  data: () => ({
		route: useRoute(),
		genericPosterBig: require('../assets/generic-poster.png'),
		movie: null as Movie | null,
		movieQuality: String as unknown | 'bad',
		movieCast: Array as unknown | MovieCast[],
		movieTrailers: Array as unknown | Trailer[],
	  }),
	  mounted() {
		this.getMovieDetails();
		this.getMovieTrailers();
		this.getMovieCast();
	  },
	  methods: {
		getMovieDetails() {
		  this.$axios
			.get(
			  `${env.api_url}/movie/${this.route.params.id}?api_key=${env.api_key}`
			)
			.then((response) => {
			  this.movie = response.data;
			  // 获取电影后,设置其质量
			  this.setMovieQuality();
			})
			.catch((err) => console.log(err));
		},
		setMovieQuality() {
		  if (Number(this.movie?.vote_average) >= 7) {
			 this.movieQuality = 'good';
		  } else if(Number(this.movie?.vote_average) < 7 && Number(this.movie?.vote_average) > 5.5) {
			 this.movieQuality =  'average';
		  } else {
			 this.movieQuality =  'bad';
		  }
		},
		getMovieTrailers() {
		  this.$axios
			.get(
			  `${env.api_url}/movie/${this.route.params.id}/videos?api_key=${env.api_key}`
			)
			.then((response) => {
			  this.movieTrailers = response.data.results;
			})
			.catch((err) => console.log(err));
		},
		getMovieCast() {
		  this.$axios
			.get(
			  `${env.api_url}/movie/${this.route.params.id}/credits?api_key=${env.api_key}`
			)
			.then((response) => {
			  this.movieCast = response.data.cast;
			})
			.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}`
		},
	  },
	  watch: {
		"$route.params.id"() {
		  this.getMovieDetails();
		  this.getMovieTrailers();
		  this.getMovieCast();
		},
	  },
	});
	</script>
在 `src\models\MovieCast.ts` 中:
	export interface MovieCast {
		adult?: boolean;
		character?: string;
		name?: string;
		profile_path?: string;
	}
	
而不是这两行代码:
	movieCast: Array as unknown | MovieCast[]
	movieTrailers: Array as unknown | Trailer[]
我*曾经*这样写过:
    movieTrailers: [],
    movieCast: [],
我做出了这个改变,以便我有一个更加**严格/强类型化**的应用程序。
### 问题
自从做出更改以来,控制台显示错误:
 > TypeError: _ctx.movieCast.slice is not a function
	<div
		v-for="actor in movieCast.slice(0, 12)"
		:key="actor['id']"
		class="col-sm-3"
	  >
		<ActorCard :actor="actor" />
	</div>
有一个**[Stackblitz](https://stackblitz.com/edit/vue3-typescript-vue-cli-starter-7y9c7v)**,其中包含工作中的应用程序。
### 问题
1. 我做错了什么?
2. 最可靠的解决方法是什么?
Please let me know if you need further assistance or have any questions.
英文:
I have been working on an SPA with Vue 3, TypeScript and The Movie Database (TMDB) API.
In src\components\MovieDetails.vue I have:
<template>
<div class="row">
<div class="col-sm-4 col-md-3">
<div class="poster-container text-center text-sm-start my-3">
<img v-if="movie"
:src="genericPoster"
:alt="movie?.title"
class="img-fluid shadow-sm"
/>
</div>
</div>
<div class="col-sm-8 col-md-9">
<h1 class="movie-title mt-4">{{ movie?.original_title }}</h1>
<p v-if="movie?.genres.length">
<strong class="pe-1">Genres:</strong>
<span
class="badge bg-secondary"
v-for="genre in movie?.genres"
:key="genre.id"
>{{ genre.name }}</span
>
</p>
<p v-if="movie?.vote_average" class="user-score">
<strong>User Score:</strong>
<span class="score" :class="movieQuality">{{
Number(movie?.vote_average).toFixed(2)
}}</span>
</p>
<div v-if="movie?.overview">
<h2 class="section-title">Overview</h2>
<p>{{ movie.overview }}</p>
</div>
<div v-if="movieTrailers.length" class="mb-3">
<h2 class="section-title">Trailers</h2>
<TrailerCarousel :movieTrailers="movieTrailers" />
</div>
<div v-if="movieCast.length" class="cast-container">
<h2 class="section-title">Cast</h2>
<div class="row">
<div
v-for="actor in movieCast.slice(0, 12)"
:key="actor['id']"
class="col-sm-3"
>
<ActorCard :actor="actor" />
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import { useRoute } from "vue-router";
import env from "../env";
import { Movie } from "@/models/Movie";
import { Trailer } from "@/models/Trailer";
import { MovieCast } from "@/models/MovieCast";
import TrailerCarousel from "./TrailerCarousel.vue";
import ActorCard from "./ActorCard.vue";
export default defineComponent({
name: "MovieDetails",
components: {
TrailerCarousel,
ActorCard,
},
data: () => ({
route: useRoute(),
genericPosterBig: require('../assets/generic-poster.png'),
movie: null as Movie | null,
movieQuality: String as unknown | 'bad',
movieCast: Array as unknown | MovieCast[]
movieTrailers: Array as unknown | Trailer[]
}),
mounted() {
this.getMovieDetails();
this.getMovieTrailers();
this.getMovieCast();
},
methods: {
getMovieDetails() {
this.$axios
.get(
`${env.api_url}/movie/${this.route.params.id}?api_key=${env.api_key}`
)
.then((response) => {
this.movie = response.data;
// Once you get the movie, set its quality
this.setMovieQuality();
})
.catch((err) => console.log(err));
},
setMovieQuality() {
if (Number(this.movie?.vote_average) >= 7) {
this.movieQuality = 'good';
} else if(Number(this.movie?.vote_average) < 7 && Number(this.movie?.vote_average) > 5.5) {
this.movieQuality =  'average';
} else {
this.movieQuality =  'bad';
}
},
getMovieTrailers() {
this.$axios
.get(
`${env.api_url}/movie/${this.route.params.id}/videos?api_key=${env.api_key}`
)
.then((response) => {
this.movieTrailers = response.data.results;
})
.catch((err) => console.log(err));
},
getMovieCast() {
this.$axios
.get(
`${env.api_url}/movie/${this.route.params.id}/credits?api_key=${env.api_key}`
)
.then((response) => {
this.movieCast = response.data.cast;
})
.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}`
},
},
watch: {
"$route.params.id"() {
this.getMovieDetails();
this.getMovieTrailers();
this.getMovieCast();
},
},
});
</script>
In src\models\MovieCast.ts:
export interface MovieCast {
adult?: boolean;
character?: string;
name?: string;
profile_path?: string;
}
In stead of these 2 lines in the components
movieCast: Array as unknown | MovieCast[]
movieTrailers: Array as unknown | Trailer[]
I used to have:
movieTrailers: [],
movieCast: [],
I have made the change so that I would have a more strictly/strongly typed app.
The problem
Since the change, the console shows the error:
> TypeError: _ctx.movieCast.slice is not a function
<div
v-for="actor in movieCast.slice(0, 12)"
:key="actor['id']"
class="col-sm-3"
>
<ActorCard :actor="actor" />
</div>
There is a Stackblitz too, with the working app.
Questions
- What am I doing wrong?
 - What is the most reliable way to fix this issue?
 
答案1
得分: 1
If you are assigning a non-array, you will also get an error. Try logging the type, and see what is actually returned.
console.log('movieCast', response.data.cast)
this.movieCast = response.data.cast;
Use a conditional operator to prevent an error while your async value is undefined and calculating. You could also wrap the v-for in a v-if and check for undefined.
v-for="actor in movieCast?.slice(0, 12)"
英文:
If you are assigning a non-array, you will also get an error. Try logging the type, and see what is actually returned.
console.log('movieCast', response.data.cast)
this.movieCast = response.data.cast;
Use a conditional operator to prevent an error while your async value is undefined and calculating.  You could also wrap the v-for in a v-if and check for undefined.
v-for="actor in movieCast?.slice(0, 12)"
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论