0

I'm trying to get width value from Template and pass the value to child component in vue/nuxt.

However, it doesn't work out. I assume that is because width from template is obtained later stage in life cycle hook.

How could I ensure the width value is obtained first and the child element gets the information?

Parent

<template>
  <v-col cols="12" sm="12" md="4" ref="demandContainer">
    <div class="chart-container p-4 dark-background">
      <div class="font-weight-bold">Demand</div>
      <area-chart
        v-if="trendsData.length > 0"
        :data="trendsData"
        :baseWidth="width"
      >
      </area-chart>
    </div>
  </v-col>
</template>

<script>
export default {
  updated() {
    this.width = this.$refs.demandContainer.$el.clientWidth
  },
}
</script>

Child

<svg ref="svg-area" :width="baseWidth" :height="baseHeight">
</svg>

Error Message enter image description here

kissu
  • 40,416
  • 14
  • 65
  • 133
Soonk
  • 332
  • 2
  • 14

2 Answers2

0

for the $refs to work you should make sure the template is fully rendered to the DOM and to do this you can have this code in the mounted hook:

mounted() {
  this.$nextTick(() => { this.width = this.$refs.demandContainer.$el.clientWidth; });
}

according to vue doc for $nextTick:

vm.$nextTick( [callback] ): Defer the callback to be executed after the next DOM update cycle. Use it immediately after you’ve changed some data to wait for the DOM update. This is the same as the global Vue.nextTick, except that the callback’s this context is automatically bound to the instance calling this method.

so with the help of $nextTick you can make sure the element is rendered to DOM to use the ref defined on the element.

Edit

actually it is $el thats undefined. this.$refs.demandContainer returns the element itself and to get the width you can just have this.$refs.demandContainer.clientWidth.

check the example below:

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  mounted() {
    console.log(this.$refs.demandContainer.clientWidth);
  },
})
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>

<div id="app">
  <v-app>
    <v-main>
      <v-container>
        <v-col cols="12" sm="12" md="4" ref="demandContainer">
          <div class="chart-container p-4 dark-background">
            <div class="font-weight-bold">Demand</div>
          </div>
        </v-col>
      </v-container>
    </v-main>
  </v-app>
</div>
hamid niakan
  • 2,668
  • 1
  • 12
  • 25
0

Refs are available only once the component is mounted to the actual DOM.
In created, it is created somewhere but not yet appended to your page.

The issue as explained here is that you may have some issues to pass it to the child component since child's mounted lifecycle hook will happen before the parent's one.

I've tried some super hacky things

async mounted() {
  await this.$nextTick()
  this.$watch(
    () => {
      return this.$refs.test.$el.clientWidth
    },
    (val) => {
      console.log('nice', val)
    },
  )
},

It did not work because clientWidth would be available at an even later point (lifecycle-wise). So, the last solution is probably to use some even more ugly setTimeout.


Or you could fix the issue with CSS, it will be:

  • faster
  • less heavy on the webpage
  • more adapted
  • less error prone

Because SVG are meant to be super responsive and easy to work with.
Can you show us what you want to do with your SVG?

TLDR: don't use JavaScript to solve a responsive issue here. Having to work with parent + child + mounted lifecycles is not a good idea usually.

tony19
  • 125,647
  • 18
  • 229
  • 307
kissu
  • 40,416
  • 14
  • 65
  • 133