46

I have a reference to a component

<Gmap ref="mapRef">

In mounted I am doing this, to see the objects are available

mounted(){
    let self = this
    console.log(self.$refs) // Shows the mapRef object reference
    console.log(self.$refs.mapRef) // returns undefined ???
}

self.$refs shows...

  mapRef: VueComponent {_uid: 207, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}

So then why does self.$refs.mapRef return undefined?? Even though it's clearly there??

Asef Hossini
  • 655
  • 8
  • 11
Kylie
  • 11,421
  • 11
  • 47
  • 78
  • What about plain old `this.$refs`? – ceejayoz Jan 24 '19 at 21:25
  • Can you make a snippet or fiddle that demonstrates the problem? I can't replicate it in a snippet. – Roy J Jan 24 '19 at 21:30
  • this.$refs is the same thing. So no...it shows the same thing, Also...no I cant replicate it in JsFiddle either. Its just in my project that its happening. Ugh....so frutstrating – Kylie Jan 24 '19 at 21:32
  • 8
    _Note that mounted does not guarantee that all child components have also been mounted_. [docs](https://vuejs.org/v2/api/#mounted) – ljubadr Jan 24 '19 at 21:38
  • @ljubadr - actually according to [this](https://medium.com/@brockreece/vue-parent-and-child-lifecycle-hooks-5d6236bd561f) it waits. Can you provider any doc to support your statement ? – Beniamin H Jan 24 '19 at 21:40
  • 1
    I did provide the link, but here it is again [mounted docs](https://vuejs.org/v2/api/#mounted) – ljubadr Jan 24 '19 at 21:41
  • so medium's article seems to be not correct – Beniamin H Jan 24 '19 at 21:43
  • Yup, always check the docs first, had similar experience with random articles – ljubadr Jan 24 '19 at 21:44
  • 1
    Instead of `console.log()` use `debugger` (or place a breakpoint), and inspect the values – ljubadr Jan 24 '19 at 21:47
  • 3
    You can look into [this question](https://stackoverflow.com/questions/23392111/console-log-async-or-sync). `console.log` has some quirks – ljubadr Jan 24 '19 at 21:52

8 Answers8

48

I solved this by using v-show instead of v-if.

I had the component inside a v-if statement.

 <div v-if="!isLoading"> 
   <GMap ref="mapRef" />
 </div>

I just changed that to v-show

<div v-show="!isLoading"> 
   <GMap ref="mapRef" />
 </div>

And now the object is available in mounted(). Still find it strange that the console.log(this.$refs) showed it being available as a key on this.$refs, even though it actually wasn't? Thats strange behaviour.

The other wierd thing was, that even if I tried to access this.$refs.mapRef in my data loading section, AFTER THE DATA WAS LOADED, (ie after isLoading=false), I still couldn't access it. Even though then, it should've been available because the v-if passed.

So v-show solved it, by just hiding the div, instead of not rendereding it. Stupid little workaround.

Kylie
  • 11,421
  • 11
  • 47
  • 78
  • 2
    `this.$refs` is filled after being mounted. The console.log shows the current version of an object, when you open it. Try the following: `let a = {foo: 1}; console.log(a); a.bar = 2;` It will result in an a with both foo and bar properties. – StefanJanssen May 14 '21 at 08:40
  • This is obviously way late, but for anyone else reading that last comment from @StefanJanssen the way you can make sure to `console.log()` and get the current value is to use `JSON.stringify()` like: `console.log(JSON.stringify(this.$refs))` as that will output a string that can't get updated in the console like Stefan's example. – Tomas May 16 '23 at 15:32
20

I had a similar problem getting a ref to a leaflet map instance, try waiting for the "nextTick"

mounted(){
  this.$nextTick(()=>{
    let self = this
    console.log(self.$refs) // Shows the mapRef object reference
    console.log(self.$refs.mapRef) // returns undefined ???
  });
}

see the docs for more- https://v2.vuejs.org/v2/api/#vm-nextTick and https://v2.vuejs.org/v2/api/#mounted

tony19
  • 125,647
  • 18
  • 229
  • 307
chrismarx
  • 11,488
  • 9
  • 84
  • 97
  • 7
    nah, still returns undefined. Im stumped :( – Kylie Jan 24 '19 at 21:37
  • Ive tried jsFiddle. And it works there. It appears to only be happening in my project unfortunately. Im using vue2-google-maps component for the map. So maybe its something to do with that particular components lifecycle or something like that?? Not too sure. – Kylie Jan 24 '19 at 22:06
  • I looked at the vue2-google-maps repo, and it looks like a similar issue was addressed with nextTick being the main answer, but maybe some of the responses here will help - https://github.com/xkjyeah/vue-google-maps/issues/260 – chrismarx Jan 24 '19 at 22:43
  • 3
    I solved it, the referenced GMap was inside a v-if statement. I changed it to v-show and it works now. Wierd that the console.log(this.$refs) still showed it as "being there" though. – Kylie Jan 24 '19 at 22:59
14

Main solution is to make sure your component is not imported dynamically

You'd still need to also avoid wrapping your component with v-if and use this.$refs.mapRef in mounted(), instead of created()

I wasn't using Vue Google Maps plugin/component, but my own custom component


Method 1

Use:

import CustomComponent from './CustomComponent ';

export default {
  components: {
    CustomComponent,
  },
  ...
}

Instead of

export default {
  components: {
    CustomComponent: () => import('./CustomComponent'),
  },
  ...
}

Method 2

Use this.$refs.mapRef in mounted() instead of created().

This way, setTimeout() and this.$nextTick() not needed

Correct way:

mounted(){
  //works! child components were loaded
  console.log(this.$refs.mapRef) 
}

Wrong way:

created(){
  //DON'T DO THIS! child components hasn't loaded
  console.log(this.$refs.mapRef) 

  //might work but it's an unreliable workaround
  setTimeout(()=>{
    console.log(this.$refs.mapRef)
  }, 0);

  //might work but it's an unreliable workaround
  this.$nextTick(()=>{
    console.log(this.$refs.mapRef)
  });

}

Method 3

Do not wrap child component using v-if, use v-show instead.

Correct way:

 <div v-show="!isLoading"> 
   <GMap ref="mapRef" />
 </div>

Wrong way:

 <div v-if="!isLoading"> 
   <GMap ref="mapRef" />
 </div>

Other Methods (not proper & not a solution)

The following are methods I tried but didn't work until I used the methods on top (stopped using dynamic import)

I already put this.$refs.mapRef in mounted() tried to wrap it with setTimeout() and 2 layers of this.$nextTick().

It works only on hot reload, but no longer works after a page refresh

mounted(){
  setTimeout(()=>{
    this.$nextTick(()=>{
      this.$nextTick(()=>{
        console.log(this.$refs.mapRef) // returns undefined
      });
    });
  }, 0);
}

Thanks to this comment in another answer: How to access to a child method from the parent in vue.js

Nickson Yap
  • 1,146
  • 15
  • 23
0

You can wrap your function in the $mapPromise method provided by vue2-google-maps, which will ensure that the Map has fully rendered before running your code:

let self = this;
self.$refs.mapRef.$mapPromise.then(() => {
    console.log(self.$refs.mapRef);
})

This should solve any problem where your code relies the Google Map to be fully rendered in order to run.

*NOTE: $mapPromise was named $mapCreated in past versions.

See Docs: https://github.com/Jeson-gk/vue2-google-maps/blob/master/API.md#mapcreated--promisegooglemapsmap

-

The reason why console.log(self.$refs); was showing the value when you looked, is that self.$refs is passed by reference. So, by the time you viewed the console, the rest of the data finished loading in a matter of milliseconds. And self.$ref.mapRef is not passed by reference, so it remains undefined, as it was during runtime.

Try adding a breakpoint directly after console.log(self.$refs); and before console.log(self.$refs.mapRef); and you will probably find that the data had not loaded.

0

In my case, I was using ref along with v-for, I missed the fact ref is generated only after v-for is rendered in the dom, and I was using v-for with a state array property which is populated a few seconds after mounted is called and I was trying to access my ref before the populating the state array.

Viraj Singh
  • 1,951
  • 1
  • 17
  • 27
0

Seems like you're using: https://github.com/diegoazh/gmap-vue, as the docs says: https://diegoazh.github.io/gmap-vue/#lazy-loading

async mounted() {
    await this.$gmapApiPromiseLazy()

    ...
}

This works perfectly.

Roy
  • 4,254
  • 5
  • 28
  • 39
0

You could add eager prop to the component, because of lazy loading in vuetify 2.

XMehdi01
  • 5,538
  • 2
  • 10
  • 34
-5

You can use it as follows, though I don't know if in anyway it could break typescript implementation for commercial projects.

(this as any).$refs.mapRef
Yogesh Vadekar
  • 119
  • 2
  • 9