“在这个Vue 3和TypeScript应用中,是什么导致了切片不是一个函数的错误?”

huangapple go评论65阅读模式
英文:

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: [],

我做出了这个改变,以便我有一个更加**严格/强类型化**的应用程序。


### 问题

自从做出更改以来,控制台显示错误:

 &gt; 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:

&lt;template&gt;
&lt;div class=&quot;row&quot;&gt;
&lt;div class=&quot;col-sm-4 col-md-3&quot;&gt;
&lt;div class=&quot;poster-container text-center text-sm-start my-3&quot;&gt;
&lt;img v-if=&quot;movie&quot;
:src=&quot;genericPoster&quot;
:alt=&quot;movie?.title&quot;
class=&quot;img-fluid shadow-sm&quot;
/&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;col-sm-8 col-md-9&quot;&gt;
&lt;h1 class=&quot;movie-title mt-4&quot;&gt;{{ movie?.original_title }}&lt;/h1&gt;
&lt;p v-if=&quot;movie?.genres.length&quot;&gt;
&lt;strong class=&quot;pe-1&quot;&gt;Genres:&lt;/strong&gt;
&lt;span
class=&quot;badge bg-secondary&quot;
v-for=&quot;genre in movie?.genres&quot;
:key=&quot;genre.id&quot;
&gt;{{ genre.name }}&lt;/span
&gt;
&lt;/p&gt;
&lt;p v-if=&quot;movie?.vote_average&quot; class=&quot;user-score&quot;&gt;
&lt;strong&gt;User Score:&lt;/strong&gt;
&lt;span class=&quot;score&quot; :class=&quot;movieQuality&quot;&gt;{{
Number(movie?.vote_average).toFixed(2)
}}&lt;/span&gt;
&lt;/p&gt;
&lt;div v-if=&quot;movie?.overview&quot;&gt;
&lt;h2 class=&quot;section-title&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;{{ movie.overview }}&lt;/p&gt;
&lt;/div&gt;
&lt;div v-if=&quot;movieTrailers.length&quot; class=&quot;mb-3&quot;&gt;
&lt;h2 class=&quot;section-title&quot;&gt;Trailers&lt;/h2&gt;
&lt;TrailerCarousel :movieTrailers=&quot;movieTrailers&quot; /&gt;
&lt;/div&gt;
&lt;div v-if=&quot;movieCast.length&quot; class=&quot;cast-container&quot;&gt;
&lt;h2 class=&quot;section-title&quot;&gt;Cast&lt;/h2&gt;
&lt;div class=&quot;row&quot;&gt;
&lt;div
v-for=&quot;actor in movieCast.slice(0, 12)&quot;
:key=&quot;actor[&#39;id&#39;]&quot;
class=&quot;col-sm-3&quot;
&gt;
&lt;ActorCard :actor=&quot;actor&quot; /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/template&gt;
&lt;script lang=&quot;ts&quot;&gt;
import { defineComponent } from &quot;vue&quot;;
import { useRoute } from &quot;vue-router&quot;;
import env from &quot;../env&quot;;
import { Movie } from &quot;@/models/Movie&quot;;
import { Trailer } from &quot;@/models/Trailer&quot;;
import { MovieCast } from &quot;@/models/MovieCast&quot;;
import TrailerCarousel from &quot;./TrailerCarousel.vue&quot;;
import ActorCard from &quot;./ActorCard.vue&quot;;
export default defineComponent({
name: &quot;MovieDetails&quot;,
components: {
TrailerCarousel,
ActorCard,
},
data: () =&gt; ({
route: useRoute(),
genericPosterBig: require(&#39;../assets/generic-poster.png&#39;),
movie: null as Movie | null,
movieQuality: String as unknown | &#39;bad&#39;,
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) =&gt; {
this.movie = response.data;
// Once you get the movie, set its quality
this.setMovieQuality();
})
.catch((err) =&gt; console.log(err));
},
setMovieQuality() {
if (Number(this.movie?.vote_average) &gt;= 7) {
this.movieQuality = &#39;good&#39;;
} else if(Number(this.movie?.vote_average) &lt; 7 &amp;&amp; Number(this.movie?.vote_average) &gt; 5.5) {
this.movieQuality =  &#39;average&#39;;
} else {
this.movieQuality =  &#39;bad&#39;;
}
},
getMovieTrailers() {
this.$axios
.get(
`${env.api_url}/movie/${this.route.params.id}/videos?api_key=${env.api_key}`
)
.then((response) =&gt; {
this.movieTrailers = response.data.results;
})
.catch((err) =&gt; console.log(err));
},
getMovieCast() {
this.$axios
.get(
`${env.api_url}/movie/${this.route.params.id}/credits?api_key=${env.api_key}`
)
.then((response) =&gt; {
this.movieCast = response.data.cast;
})
.catch((err) =&gt; console.log(err));
},
},
computed: {
genericPoster() {
return !this.movie?.poster_path
? this.genericPosterBig
: `https://image.tmdb.org/t/p/w500/${this.movie?.poster_path}`
},
},
watch: {
&quot;$route.params.id&quot;() {
this.getMovieDetails();
this.getMovieTrailers();
this.getMovieCast();
},
},
});
&lt;/script&gt;

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

&lt;div
v-for=&quot;actor in movieCast.slice(0, 12)&quot;
:key=&quot;actor[&#39;id&#39;]&quot;
class=&quot;col-sm-3&quot;
&gt;
&lt;ActorCard :actor=&quot;actor&quot; /&gt;
&lt;/div&gt;

There is a Stackblitz too, with the working app.

Questions

  1. What am I doing wrong?
  2. 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(&#39;movieCast&#39;, 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=&quot;actor in movieCast?.slice(0, 12)&quot;

huangapple
  • 本文由 发表于 2023年5月25日 22:06:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76333211.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定