Home > Blockchain >  Trigger method on all components inside of a v-for
Trigger method on all components inside of a v-for

Time:07-11

I have an audio player component that needs to send a message to all other players to pause themselves when it is clicked. However, there's a variable amount of components all inside of a v-for loop. It looks something like this:

<template>
   <AudioPlayer v-for="song in songs" :key="song.id" @play="pauseOthers" />
</template>

<script>
import { defineComponent, ref } from 'vue';
import AudioPlayer from '@/components/AudioPlayer.vue';

export default defineComponent({
   setup(){
      var songs = [{id: "song1"},{id: "song2"}]
      
      const pauseOthers = () => {
         //this is the part that I need to figure out
      }

      return { songs, pauseOthers }
   }
})
</script>

The components's script looks like

<script>
import { defineComponent, ref } from 'vue';

export default defineComponent({
   setup(){
      const pause = () => {
         console.log("Paused!");
      }
      
      return { pause }
   }
})
</script>

Ideally this could avoid using third-party packages, but it's fine if there really is no easy way.

CodePudding user response:

Basically what you are going to want to do is create a a ref on the component which contains the v-for loop (i.e. the parent component) that is something like const currentlyPlayingPlayerIndex = ref(-1) then provide that as a prop to your AudioPlayer component. In AudioPlayer you use a watcher that watches for the value of that prop to change, if the index of that player (order in the v-for list) matches the value of currentlyPlayingPlayerIndex then take whatever action is required to play the audio. If it doesn't match, take whatever action is required to pause the audio. This will ensure that only one player is ever playing at once.

Within the player component the "pause" button should emit an event that sets the value of currentlyPlayingPlayerIndex to -1 or null or something that is outside the bounds of the range of the indexes. The "play" button should emit an event to the parent that updates the value of currentlyPlayingPlayerIndex to the index of the player component that contained the clicked button.

In summary, don't manage the "playing" state on the level of player but instead on their parent.

CodePudding user response:

I agree with @WillD, you should manage those events from the parent or you will overcomplicate it.
Example:

<!-- Parent -->
<template>
    <AudioPlayer v-for="song in songs" :key="song.id" :song-id="song.id" @play="pauseOthers" />
</template>

<script lang="ts" setup>
    const songs = [
        { id: "song1" },
        { id: "song2" },
        { id: "song3" },
        { id: "song4" },
        { id: "song5" }
    ];

    const pauseOthers = (songId: string) => {
        const songsToStop = songs.filter(song => song.id !== songId);
        console.log("songs to stop:", songsToStop.map(song => song.id).join(" "));
    };
</script>


<!-- Audioplayer children component -->
<template>
    <button block @click="emit('play', props.songId)">
        something
    </button>
</template>

<script lang="ts" setup>
    const props = defineProps({
        songId: {
            type: String,
            required: true
        }
    });

    const emit = defineEmits(["play"]);
</script>

Note that all those props and emits are just created for being able to reproduce an example, you probably won't need them in your final code.

  • Related