2.1.10 / 2.2.0
beforeRouteEnter () methoddata () {
return {
post: null
}
},
beforeRouteEnter (to, from, next) {
getPost(to.params.id, (err, post) => {
if (err) {
// display some global error message
next(false)
} else {
next(vm => {
console.log('next function')
vm.post = post
})
}
})
},
created () and mounted () methodcreated () {
console.log('created:', this.post);
},
mounted () {
console.log('mounted:', this.post);
},
The data fetched during the beforeRouteEnter () method should be set before the created () and mounted () methods occur.
The data is null in the created () and mounted () methods.
The 'created' and 'mounted' console input will occur before the 'next function'.
This is actually expected, the callback is triggered after a nextTick that's why you don't have access to post in both hooks.
Edit: this is something we might not be able to change on v3 as it is a breaking change but it's something we plan on improving for v4
I am confused about why this behaviour would be desirable.
In my scenario, parts of my component that are determined by the fetched data (e.g. a slug for a route and props for child components).
This means that errors occur as the data is null when the component is rendered
I'm also confused! Does https://router.vuejs.org/en/advanced/data-fetching.html#fetching-before-navigation need updating then?
I second that the documentation at fetching-before-navigation then seems incorrect (and led me to this page).
@posva So if you dont have access to data set in beforeRouteEnter() on the vue instance in mounted() and created() when exactly am I supposed to access them? Purely in beforeRouteEnter() and the template?
You have access in the callback (first and only argument) of the next
function
On Tue, 13 Jun 2017, 17:20 Darren Segal, notifications@github.com wrote:
@posva https://github.com/posva So if you dont have access to data set
in beforeRouteEnter() on the vue instance in mounted() and created() when
exactly am I supposed to access them? Purely in beforeRouteEnter() and
the template?—
You are receiving this because you were mentioned.Reply to this email directly, view it on GitHub
https://github.com/vuejs/vue-router/issues/1144#issuecomment-308151921,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAoicZwjwdwF-lps635FMqTk9eHcvO-vks5sDqiegaJpZM4L6H0a
.
I'm lost here
Before, in Vue 1 you could do something like this
route: {
waitForData: true,
data(transition) {
someAjaxRequest().done(function(){
transition.next();
})
}
}
},
You could start your component with the data ready.
But now in Vue 2 is there a way to achieve this? what's the utility of beforeRouteEnter if you can't set the data in the component before the lifecycles go on?
I'm asking out of pure ignorance, this component architecture things go over my head.
I found a workaround that works for me:
BaseLazyLoadingComponent):export default (loadData) => {
let loaderCallback = () => {}
const loadRoute = (to, from, next) => {
loadData(to, (callback) => {
loaderCallback = callback
next()
})
}
return {
beforeRouteEnter: loadRoute,
beforeRouteUpdate: loadRoute,
created: function() {
loaderCallback.apply(this)
},
watch: {
'$route': function() {
loaderCallback.apply(this)
}
}
}
}
beforeRouteEnter/Update lazy loading:import BaseLazyLoadingComponent from './BaseLazyLoadingComponent.js'
export default {
data () {
return {
value: null // will always be loaded before component is first rendered
}
},
extends: BaseLazyLoadingComponent((to, callback) => {
getData(/* whatever you call to get the data for the component */, response => {
callback(function() {
this.value = response.value
// and so on, this is mapped to the vue component instance so you can assign all data
})
})
})
}
Hey @posva - it does seem like there is a possible bug here unless I'm not understanding the callback in the next() method.
I've put together a Fiddle to reproduce the issue: https://jsfiddle.net/gc3xd1oL/18/
Pull up dev tools, watch the console and click on the "Home component" link. You'll see the created() method gets hit first with a null value for this.name, followed by the callback method being called within the beforeRouteEnter() method, followed by another console log showing the data assignment does take place with a value of "Zach" shown in the log.
Am I doing everything correctly, or is there a bug?
@posva - would you mind taking a look at my reproduction of the issue? If this is an actual issue I can take a look and see if I can get a PR submitted. Was just having a hard time trying to track down where this is happening in the vue-router code.
@zawilliams That is working as expected, the created hook is triggered before next callback
I assume as well to have:
1) get data insile beforeRouteEnter and define next callback with vm parameter. Operate data in the callback and change component(vm) data as well
2) have an access to the changed data inside component created hook
Will it be changed or it will stay the same as it is?
What is the current status of this? It still seem to be an issue with Vue 2.5.2 and Vue-Router 3.0.1
It's not an issue, the created hook gets called as soon as the component can be used, and the callback passed to next is called after that (a tick after I believe) and has to because setting the local variable wouldn't have an effect if it was called before. This cannot change.
If you want to run some tasks that depend on the data fetched you should put that logic in a method and invoke the method from the callback passed to next:
export default {
data() {
return {
post: null,
}
},
beforeRouteEnter(to, from, next) {
try {
const post = await getPost(to.params.id)
next(vm => {
vm.post = post
this.manipulateData()
})
} catch (err) {
next(false)
}
},
methods: {
manipulateData() {
// this was the code initially put in created
}
}
}
you can also use a watcher to invoke that method if you have a beforeRouteUpdate hook:
export default {
data() {
return {
post: null,
}
},
beforeRouteUpdate(to, from, next) {
this.$options.beforeRouteEnter(to, from, next)
},
beforeRouteEnter(to, from, next) {
try {
const post = await getPost(to.params.id)
next(vm => {
vm.post = post
})
} catch (err) {
next(false)
}
},
watch: {
post: 'manipulateData'
},
methods: {
manipulateData() {
// this was the code initially put in created
}
}
}
I hope this gives some guidance and clears things 🙂
Sorry, but as I stated here about this issue, I still can't fetch data. They still result null. Tried with @BenRomberg solution, and by refactoring code like @posva example too, with no success.
In short, I'm trying to fetch data from DB, in order to initialize a third-party Vue framework component for a Laravel project. Ajax call goes good, but seems that the vm callback instance being ignored and property are not updated, still remains as initially declared (as null). Simply, I would that this technique works.
Actually I'm still stuck on this and I can't continue until this will be solved.
EDIT:
Problem solved. The issue was caused by a misconfiguration of the third-party component itself.
It makes sense to me on why this isn't triggered before _created_, but why does it have to trigger after _mounted_ as well ?
l hacked some code , so can no need set init data , just use async set data
set pull request
https://github.com/vuejs/vue-router/pull/2512
use like this
export default {
beforeRouteEnter:(from, to, next)->
li = (await $.get '/api/li.txt').split('\n').filter(Boolean)
@data = ->
{
n:0
now:1
li
src : [
li[0]
li[1]
]
}
next()
I just came across this as well:
I solved it with a bit of a cheat – the component CANNOT be used in two separate places – using the following solution (which I chose not to continue with) but it demonstrates that setting data before mounting makes life so much easier:
<template>
<div>
<h2>Site: {{ site.name }}</h2>
</div>
</template>
<script>
import { getSite } from 'api/sites'
let site
export default {
data () {
return {
site
}
},
async beforeRouteEnter (to, from, next) {
site = await getSite(to.params.siteId)
next()
}
}
</script>
Vue / Vue Router really does need an elegant solution to this.
I saw this post, which is a cool solution:
But for now, what about passing any data directly into the created method ?
import { getSite } from 'api/sites'
export default {
data () {
return {
site
}
},
async beforeRouteEnter (to, from, next) {
site = await getSite(to.params.siteId)
next(site)
},
created (site) {
this.site = site
}
}
Or:
async beforeRouteEnter (to, from, next, done) {
site = await getSite(to.params.siteId)
done(site)
next()
},
created (site) {
this.site = site
}
Though digging around Vue's source, I see that Vue's hooks don't support passing payloads.
So maybe set an option on the instance the created hooks run:
created () {
this.site = this.$options.async.site
// or
this.site = this.$route.async.site
}
There really should be a low-effort way to capitalise on the async nature of beforeRouteEnter so fetched data is simply available during and after created and we can avoid the additional acrobatics :(
@BenRomberg BRILLIANT!!! Works beautifully!!!
Most helpful comment
I found a workaround that works for me:
BaseLazyLoadingComponent):beforeRouteEnter/Updatelazy loading: