1

I'm trying to import a component like this for lazy loading:

components: {SizeChartForm:() => import("@/core/components/size/SizeChartForm/SizeChartForm")},

I'm having a ref to SizeChartForm component:

<SizeChartForm ref="sizeChart" v-model="value" />

and trying to use it in the mounted:

mounted() {
    this.$refs.sizeChart.setDisabled(false)
}

but it says sizeChart is undefined. Even $nextTick is not working.

But when I use normal import it works:

import SizeChartForm from "@/core/components/size/SizeChartForm/SizeChartForm";
  
components: {SizeChartForm}

Vue version: 2.6.11

2
  • Which Vue version are you using? Commented Jul 27, 2021 at 22:38
  • @DannyFeliz 2.6.11 Commented Jul 28, 2021 at 5:15

1 Answer 1

2

$nextTick is not enough - it takes much longer to load an async component.

Generally if you need to do some work when the child component is mounted, you should do it in child's mounted hook, not in parent's (docs - "Note that mounted does not guarantee that all child components have also been mounted.").

Solution 1 (only for Vue 2)

If moving the code to the child is not an option, you can use the (little documented) fact that Vue 2 lifecycle emits special events like hook:mounted for each lifecycle stage and those events can be listened in the parent: <child ref="child" @hook:mounted="onAsyncComponentMounted"></child>

Solution 2 (Vue 2 & Vue 3)

Those events were removed in Vue 3 (I'm unable to find any documentation or anything in the migration guide but I'v tested and it doesn't work) so the best approach seems to emit some custom event from the child's mounted hook and listen for it in the parent (see second example)

Update

I found this tweet from the Vue core team member Damian Dulisz explaining (see the replies) that hook:xxx events are not considered public API and are not documented intentionally. So from this POV and also considering it's removal in Vue 3, use the second solution...

Vue 2

// NOTE: this works only in Vue 2, not in Vue 3!
const child = {
  template: `
  <div>
    <input ref="input" type="text" />
  </div>
  `
}

// to simulate component async loading in browser env.
const loadComponentAsync = function() {
  return new Promise(function(resolve, reject) {
    setTimeout(() => resolve(child), 2000);
  })
}

const app = new Vue({
  el: '#app',
  components: {
    'child': () => loadComponentAsync()
  },
  methods: {
    onAsyncComponentMounted() {
      console.log(this.$refs.child.$refs.input)
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.js"></script>
<div id='app'>
  <child ref="child" @hook:mounted="onAsyncComponentMounted"></child>
</div>

Vue 3

const child = {
  template: `
  <div>
    <input ref="input" type="text" />
  </div>
  `,
  mounted() {
    this.$emit('child-mounted')
  }
}

const loadComponentAsync = function() {
  return new Promise(function(resolve, reject) {
    setTimeout(() => resolve(child), 2000);
  })
}

const app = Vue.createApp({
  components: {
    'child': Vue.defineAsyncComponent(() => loadComponentAsync())
  },
  methods: {
    onAsyncComponentMounted() {
      console.log(this.$refs.child.$refs.input)
    }
  }
}).mount('#app')
<script src="https://unpkg.com/[email protected]/dist/vue.global.js"></script>
<div id='app'>
  <child ref="child" @child-mounted="onAsyncComponentMounted"></child>
</div>

Sign up to request clarification or add additional context in comments.

1 Comment

And some context directly from Evan...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.