How to mount a vue js child component only when the parent is fully mounted – I’am using $refs

I am using Laravel 8 and InertiaJS (Vue js)

I am using the html2pdf.js to generate PDF. So I create a component for that : ExportPdf.vue

So here is the code of the component ExportPdf.vue :

<template>
  <div>
      <button class="px-6 py-3 rounded bg-gray-600 hover:bg-gray-900 text-white text-sm font-bold whitespace-no-wrap" 
        @click="exportToPDF">Expot to PDF
    </button>
  </div>
</template>

<script>
import html2pdf from 'html2pdf.js'

export default {
props:{
    area: HTMLDivElement,
    name : String,
    orientation : {
      type: String,
      default(){
        return 'landscape'
      }
    }
},
methods:{
     exportToPDF () {
          html2pdf(this.area, {
            margin: [1.5, 1],
            filename: this.name + '.pdf',
            image: { type: 'jpeg', quality: 0.98 },
            html2canvas: {scale: 2},
            jsPDF: { unit: 'cm', format: 'a4', orientation: this.orientation ,  floatPrecision: 16 }
        })
    }
}
}
</script>

And then I’m using this component inside any component which I want to extract the content into PDF file like this :

<export-pdf  name="file name" :area="$refs.content" />

AND I reference to Div I want to extract using ref like :

<div ref="content">
 <!-- Content to Extrat into PDF -->
</div>

The first time It WORKS but when I change a component (I go to another vue), It doesn’t work, So I must refresh the page.

I console log the prop (this.area which receive the $refs.content from the parent) inside the ExportPdr component => it’s undefined. I think that is because this component is mounted before the $refs.content is initialised in the parent (If I can say that)

I found a solution but In my opinion It is not perfect. Because I need to add v-if to ExportPdf component in Each Parent Component and make the boolean to true in the mounted method of the parent. This fixed the problem and the prop is not Undefined anymore. That is all to make it works without refreshing the page every time. BUT it’s tedious to add these lines every time to each parent component.

like this : Parent template :

<export-pdf v-if="isMounted" name="Liste des consommables des équipements" :area="$refs.content" />
data(){
    return {
      isMounted : false,
    }
 }, 
mounted(){
        this.isMounted = true
}

So Any suggestoin to make it better ?

Thanks.

Answer

Because

  • The child component is mounted before parent.
  • $refs is not reactivity,you should avoid accessing $refs from within templates or computed properties.

Solution 1:

    <div ref="content">
     <!-- Content to Extrat into PDF -->
    </div>
    <export-pdf  name="Liste des consommables des équipements" :area="refElement" />
<script>
export default {
    data(){
        return {
          refElement: {},
        }
     }, 
    mounted(){
        this.refElement = this.$refs.content
    }
}
</script>

Solution 2:

parent:

 <div ref="content">
 <!-- Content to Extrat into PDF -->
 </div>
 <export-pdf  name="Liste des consommables des équipements" areaRefName="content" />

children:

props:{
    areaRefName: String,
    name : String,
    orientation : {
      type: String,
      default(){
        return 'landscape'
      }
    }
},
methods:{
     exportToPDF () {
          let element = this.$parent.$refs[this.areaRefName]
          html2pdf(element , {
            margin: [1.5, 1],
            filename: this.name + '.pdf',
            image: { type: 'jpeg', quality: 0.98 },
            html2canvas: {scale: 2},
            jsPDF: { unit: 'cm', format: 'a4', orientation: this.orientation ,  floatPrecision: 16 }
        })
    }
}