I'm building my first Vue app. I have some experience with React, and I'm trying to make a simple movies list in Vue.
There are two bugs in my app - both related to the button toggling:
OnClick I want to change hideImg, and display the other image. I can only toggle it once. It refuses to return back to neutral state.
OnClick I want to toggle show the video. It works properly - opens a video on button click, and then if I click another button it closes the old video and shows new one. But once I open all 4 videos, I cannot show or close any more videos.
It seems like I can only toggle each element ONCE.
Am I missing something?
<template>
<div v-for="(name, index) in movies" :key="index" >
<div >
{{ name.name }} {{ name.duration }}
<button
@click="name.hideImg = !name.hideImg && toggle(name.id)"
>
<p v-if="name.hideImg">►</p>
<p v-else>▼</p>
</button>
</div>
<div v-show="name.id == open">
<video controls="controls" autoplay name="media">
<source
:src="require(`@/assets/movie${index 1}.mp4`)"
alt="video"
type="video/mp4"
width="500px"
/>
</video>
</div>
</div>
</template>
<script>
export default {
name: "App",
methods: {
toggle(id) {
this.open = this.open === id ? null : id;
},
},
data() {
return {
movies: [
{
name: "Pokemon",
duration: "1hr 12min",
hideImg: true,
open: null,
id: 1,
},
{
name: "Digimon",
duration: "2hr 37min",
hideImg: true,
open: null,
id: 2,
},
{
name: "Transformers",
duration: "1hr 51min",
hideImg: true,
open: null,
id: 3,
},
{
name: "Kiepscy",
duration: "1hr 51min",
hideImg: true,
open: null,
id: 4,
},
],
};
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin: 60px auto 0;
}
.watchBtn {
background-color: red;
border-radius: 10px;
margin-left: 10px;
height: 20px;
display: flex;
align-items: center;
}
.watchBtn:hover {
background-color: rgb(255, 191, 107);
cursor: pointer;
}
.movieContainer {
margin: 5px auto;
display: flex;
flex-direction: column;
}
.center {
margin: 0 auto;
display: flex;
}
.movieContainer video {
width: 500px;
}
</style>
CodePudding user response:
There are a couple quick things that may help. Your toggle()
could be cleaned up a bit. You can do both of those actions in the toggle()
method.
And you're accessing the video incorrectly. this.open
is looking for a data property named open
at the top level of your data()
, and it's not finding it. So you need to drill down into movies
to get the right property.
Also, your indices for the movies start at 1, but the loop is starting at 0, so things are off by 1.
I put together a StackBlitz so you can see what I'm talking about.
Here's the code for posterity:
<template>
<div v-for="(movie, index) in movies" :key="index" >
<div >
{{ movie.name }}
<button
@click="toggle(movie.id)"
>
<p v-if="movie.hideImg">►</p>
<p v-else>▼</p>
</button>
</div>
<div v-show="movie.open">
<!-- <video controls="controls" autoplay name="media">
<source
:src="require(`@/assets/movie${index 1}.mp4`)"
alt="video"
type="video/mp4"
width="500px"
/>
</video> -->
<h2>It's Working!</h2>
</div>
</div>
</template>
<script>
export default {
name: "App",
methods: {
toggle(id) {
this.movies[id - 1].hideImg = !this.movies[id - 1].hideImg
this.movies[id - 1].open = !this.movies[id - 1].open
},
},
data() {
return {
movies: [
{
name: "Pokemon",
duration: "1hr 12min",
hideImg: true,
open: false,
id: 1,
},
{
name: "Digimon",
duration: "2hr 37min",
hideImg: true,
open: false,
id: 2,
},
{
name: "Transformers",
duration: "1hr 51min",
hideImg: true,
open: false,
id: 3,
},
{
name: "Kiepscy",
duration: "1hr 51min",
hideImg: true,
open: false,
id: 4,
},
],
};
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin: 60px auto 0;
}
.watchBtn {
background-color: red;
border-radius: 10px;
margin-left: 10px;
height: 20px;
display: flex;
align-items: center;
}
.watchBtn:hover {
background-color: rgb(255, 191, 107);
cursor: pointer;
}
.movieContainer {
margin: 5px auto;
display: flex;
flex-direction: column;
}
.center {
margin: 0 auto;
display: flex;
}
.movieContainer video {
width: 500px;
}
</style>
I commented out the video for testing because I don't have it locally. But I think with this the toggle buttons work as expected.