Reputation: 6462
I have a <div id="map" />
I have a mounted method in which I initialize map.
mounted(){
this.map = L.map(this.mapId, { preferCanvas: true }).setView(
[this.initialLatitudeOriginalPoint, this.intiialLongitudeOriginalPoint],
3
)
L.tileLayer(
tileconfig + '?access_token=' + key
{ maxZoom: 25, id: 'mapbox.streets' }
).addTo(this.map)
}
I have a prop called markers. and I have a watcher for it with immediate true
markers:{
handler(newVal, oldVal){
this.showMarkers()
},
immediate: true
},
Question- Looks like watcher
gets called first. before mounted. Then showMarkers
function throws error as map won't be defined because watcher
got first before mounted
. I really need immediate true for that watcher. Is there any way I can know that until map is not defined, watcher should wait?
What I think of: I think of using nextTick
. but I don't understand one point about it. If I write this.$nextTick in my watcher's handler function, when will that nextTick callback be called? after dom got updated in this specific component or even in parent and parents of parents? if it doesn't care current component, then nextTick might be a little bit wrong in my case. I just want to understand this last thing about nextTick. any clue?
Upvotes: 0
Views: 508
Reputation: 29092
Firstly, you are correct that immediate
will cause the watch
to be triggered before the component is mounted. The role of watch
is primarily to perform updates to properties based on other properties. So it will wait for all the basic properties (props, data, etc.) to be initialised but it assumes that rendering will need the output of the watch
to render correctly. In your case this isn't true.
The way I would write this is:
mounted () {
// ... map stuff ...
this.showMarkers()
},
watch: {
markers: 'showMarkers'
}
If there needs to be a nextTick
in there I'd be inclined to do that inside showMarkers
rather than making it the caller's problem.
However, you don't have to do it this way. You can do it using immediate
and nextTick
, as suggested in the question.
So the key thing is to understand when exactly nextTick
is called.
If a rendering dependency changes Vue will not re-render the component immediately. Instead it's added to a queue. Once all your other code has finished running Vue will process that queue. This is more complicated than it sounds as various components within the parent/child hierarchy may need updating. Updating a parent may result in the child no longer existing.
Once the queue has been processed and the DOM updated, Vue will call any nextTick
callbacks. Any further changes that occur inside the callback will be added to a new rendering queue.
There are a couple of key points to note here.
nextTick
callbacks. So if nextTick
triggers further updates the user will not see the interim state of the page.So let's consider the following:
markers:{
async handler(newVal, oldVal){
await this.$nextTick()
this.showMarkers()
},
immediate: true
},
I've used async
/await
here rather than passing a callback but the end result is the same.
When the handler
is called the first time it is before the component has rendered. The $nextTick
will be called after all rendering has completed (including other components). So long as other components don't assume that the markers already exist this shouldn't be a problem, they'll still be created before the user sees anything.
When markers
subsequently changes it may not actually trigger any rendering within Vue. Unless you're using markers
somewhere within one of your templates it won't be a rendering dependency so no new rendering will be triggered. That's not necessarily a problem as $nextTick
will still work even if there's nothing for Vue to render.
Upvotes: 2