-
-
Notifications
You must be signed in to change notification settings - Fork 5k
Data is not set with beforeRouteEnter () before created () method is called #1144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This is actually expected, the callback is triggered after a 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 |
You have access in the callback (first and only argument) of the next
function
…On Tue, 13 Jun 2017, 17:20 Darren Segal, ***@***.***> 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
<#1144 (comment)>,
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
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:
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)
}
}
}
}
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 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 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 |
I assume as well to have:
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 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 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 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 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 use like this
|
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 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 () {
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 |
@BenRomberg BRILLIANT!!! Works beautifully!!! |
The following works with vue 3 and vue-router 4
export default {
async preload (to, from) {
const user = await axios.get('/users/' + to.params.user);
const posts = await axios.get('/posts');
return { user, posts }
},
created () {
console.log(this.$route.preload) // { user: {...}, posts: [...] }
}
}
router.beforeEach((to, from, next) => {
// get all matched components
const components = to.matched.map(route => Object.values(route.components)).flat();
// execute "preload" (if exists)
const requests = components.map(component => component.preload?.(to, from));
// merge all results into a single object
const onSuccess = data => to.preload = data.reduce((result, item) => ({ ...result, ...item }), {});
// handle error
const onError = error => {}
// requests promise
Promise.all(requests).then(onSuccess).catch(onError).finally(next);
}) |
@BenRomberg How does your solution work given the latest Vue 3 ignores Adding to previous comments, I'm perplexed that the ability to mixin/extend for route-level guards has been dismissed out of hand in Vue 3. I have tried 3x to bring our moderately sized app up to Vue 3 but each time I'm stopped because we leverage this feature rather heavily. Two heavily commented-on threads address this now, including this one: vuejs/router#454 EDIT |
Looking forward to v4 :) |
Yes, the best would be an analog of serverPrefetch for client: clientPrefetch, which will WAIT until awaits inside are completed like it's server analog does. |
Looking forward to v4 :) |
Vue.js / vue-router versions
2.1.10 / 2.2.0
Steps to reproduce
beforeRouteEnter ()
methodcreated ()
andmounted ()
methodWhat is Expected?
The data fetched during the
beforeRouteEnter ()
method should be set before thecreated ()
andmounted ()
methods occur.What is actually happening?
The data is null in the
created ()
andmounted ()
methods.The 'created' and 'mounted' console input will occur before the 'next function'.
The text was updated successfully, but these errors were encountered: