TypeError: Cannot read properties of undefined - using ref property of imported component in v-if (vue3)

52 Views Asked by At

I am trying to integrate a audio player into a vue3 site. The component I aim to use is this one Link.

I trying to create a play/pause button that I can place anywhere on my site (separately from the player). I use v-if to switch what the button displays (depending on whether sound is played or not), and use the "isPlaying" property that I get from the player.

What I now struggle with is that when I load the page, caused by line 2 below: "!this.$refs.audioPlayer.isPlaying"

TypeError: Cannot read properties of undefined (reading 'isPlaying')

My understanding so far is that the the ref is not yet available when the component gets rendered, for which it causes an issue. I tried using different approaches (mounted, watchers, setup..) but I keep banging my head against the wall.

Any pointers how to address this correctly would be appreciated.

Funny enough: When I change "!this.$refs.audioPlayer.isPlaying" simply to "true" when loading the page, and then replacing the "true" again with the "!this.$refs.audioPlayer.isPlaying" when the page is already loaded, I can see that the button works correctly..

Many thanks!

<template>
  <div v-if="!this.$refs.audioPlayer.isPlaying" class="audio__play-start"  @click.stop="this.$refs.audioPlayer.play"><q-icon name="play_circle"></q-icon></div>
  <div v-else class="audio__play-pause" @click.stop="this.$refs.audioPlayer.pause"><q-icon name="pause_circle"></q-icon></div>
  
  <div>
    <audio-player
      ref="audioPlayer"
      :audio-list="audioList.map(elm => elm.url)"
      theme-color="black"
      :show-prev-button="false"
      :show-next-button="false"
      :show-play-button="false"
      :show-volume-button="false"
      :show-playback-rate="false"
    />
  </div>
</template>

<script lang="ts">

export default {
  data() {
    return { audioList: [{ name: 'audio1', url: 'rock.mp3'}]}
  },
}
</script>

the AudioPlayer I import in my main.ts:

import AudioPlayer from '@liripeng/vue-audio-player'
app.use(AudioPlayer);
2

There are 2 best solutions below

1
yoduh On

You're right $refs.audioPlayer is not available when the v-if executes so it immediately produces an error. You could try using && in the v-if to check that this.$refs.audioPlayer is truthy before trying to check for the isPlaying property.

<div v-if="this.$refs.audioPlayer && !this.$refs.audioPlayer.isPlaying"
0
Benjamin On

I solved it!

I had to 1) create include the audioplayer's ref in the setup(), and 2) use a computed attribute (as yoduh had suggested in his comment to my question).

Thanks!

<template>
  <div v-if="!isPlaying" class="audio__play-start"  @click.stop="this.$refs.audioPlayer.play"><q-icon name="play_circle"></q-icon></div>
  <div v-else class="audio__play-pause" @click.stop="this.$refs.audioPlayer.pause"><q-icon name="pause_circle"></q-icon></div>
  
  <div>
    <audio-player
      ref="audioPlayer"
      :audio-list="audioList.map(elm => elm.url)"
      theme-color="black"
      :show-prev-button="false"
      :show-next-button="false"
      :show-play-button="false"
      :show-volume-button="false"
      :show-playback-rate="false"
    />
  </div>
</template>

<script lang="ts">
import { ref, computed } from 'vue';

export default {
  
  setup () {
    const audioPlayer =  ref(Object)
    const isPlaying = computed(() => {  
      return audioPlayer.value.isPlaying
    })

    return {
      audioList: [{ url: './api/soundfiles?path=/tmp&filename=rock.mp3'}],
      audioPlayer,
      isPlaying
    }
  },

}
</script>