How to watch on Route changes with Nuxt and asyncData - vue.js

Hi everybody i'm trying to watch on route changes in my nuxt js app.
Here my middleware:
export default function ({ route }) {
return route; but i don't know what to write here
}
index.vue File
middleware: [routeReact]
i'm trying to write this:
app.context.route = route
but it says to me that app.context doesn't exist
Here's the point of my question i'm trying to update my data that gets from my api with axios on page if route changing
like this
this the page
i'm clicking link to next page :
but when i'm route to next page, nothing happens all data is the same:
here my asyncData code:
asyncData({ app }) {
return app.$axios.$get('apps/' + app.context.route.fullPath.replace(/\/categories\/?/, ''))
.then(res => {
return {
info: res.results,
nextPage: res.next,
prevPage: res.prev
};
})
}
Thanks for your help

First thing, context.route or it's alias this.$route is immutable object and should not be assigned a value.
Instead, we should use this.$router and it's methods for programmatic navigation or <nuxt-link> and <router-link>.
As I understand, you need to render the same route, but trigger asyncData hook in order to update component's data. Only route query is changed.
Correct way to navigate to the same page but with different data is to use link of such format:
<nuxt-link :to="{ name: 'index', query: { start: 420 }}"
Then you can use nuxt provided option watchQuery on page component and access that query inside asyncData as follows:
watchQuery: true,
asyncData ({ query, app }) {
const { start } = query
const queryString = start ? `?start=${start}` : ''
return app.$axios.$get(`apps/${queryString}`)
.then(res => {
return {
info: res.results,
nextPage: res.next,
prevPage: res.prev
}
})
},
This option does not require usage of middleware. If you want to stick to using middleware functions, you can add a key to layout or page view that is used. Here is an example of adding a key to default layout:
<nuxt :key="$route.fullPath" />
This will force nuxt to re-render the page, thus calling middlewares and hooks. It is also useful for triggering transitions when switching dynamic routes of the same page component.

Related

How to write a single piece of code running both on route A (mounted hook) and also when arriving at route A?

Currently in order for me to do a thing X either when loading route A or arriving to route A, I need to write X twice:
watch: {
$route (to, from){
if (to.name === 'simulation-step-3-sequence') {
EventBus.$emit('actionIsSortable');
}
}
},
async mounted () {
if (this.$route.name === 'simulation-step-3-sequence') {
EventBus.$emit('actionIsSortable');
}
}
Is there a way to simplify this so I write X (the emit line) only once?
This technique is really needed only in two cases - multiple routes are using same component or dynamic routes (with parameters) ...see the docs
In this cases when the new route is using same component as the old route, Vue Router will just reuse existing component instance.
You can place a key on router-view and disable this behavior. Your site will be less effective but you can get rid of $route watcher
<router-view :key="$route.fullPath" />
Other option is to change watcher definition like this:
watch: {
$route: {
immediate: true,
handler: function(to, from) {
console.log(`Route changing from '${from}' to '${to}'`);
}
}
}
Vue will call watcher handler on route changes but also when the component is created (so you can remove the code in lifecycle hook)

Vue: same route same component, but different params

I'm creating a SPA where after each HTTP response, the app goes to the same component with the same route, but the props passed to the component will be different based on the response. I understand the reuse feature in Vue and it won't reload. But I need the newly returned data every time I trigger the router to go to the component. My app.vue:
upload(formData).then(x => {
router.push({
name: 'Chart',
params: {
chartData: x
}
});
});
Then the target Chart.vue:
props: ['chartData'],
data: function() {
// do some processing using props
return {...}
}
My problem is that since for my router, the route doesn't change, hence other approaches like using :key=$route.fullPath or the beforeRouteUpdate don't work. Then how can I make the Chart component recompute the data? I've checked thru computed() and watch as well but it's not working.

how to make a component to get async data and to render server side?

We know that only router component may request asyncData in a ssr environment. i have one main component(not router component), i need some async data to render server side. because it is not router component, so i can't use asyncData for server side rendering.
so i have used created hook for calling async api, but component hook is synchronous and don't wait for promise. what to do for getting async data on server side?
App.vue - Main Component
export default {
components: {
footer: Footer,
header: Header,
selector: selector
},
beforeMount() {
// need preload metadata here
},
created () {
// it return preload metadata as response.
return this.$store.dispatch('GET_PRELOAD_META_DATA');
}
}
Action.js
GET_PRELOAD_META_DATA: ({ commit }) => {
return axios.get("api/preload").then(({ data }) => {
commit('SET_PRELOAD_DATA', data);
});
},
As of Vue 2.6, there is a new SSR feature that may accomplish what you’re trying to do. ServerPrefetch is now a hook that can resolve promises to get async data and can interact with a global store.
Check out more in their documentation. https://ssr.vuejs.org/api/#serverprefetch
(I know this an old post, but I stumbled upon it while googling and thought I might be able to help someone else googling too)

VueJS - requiring and rendering a component programmatically

I have a bit of a specific usecase here. I have a large Vue app with lots of pages. Each page has a page-id to identify it on the server and load it's content. These ids are in the router path (parent-id/child-id/another-child-page-id). On the page components themselves I display a page title. Unfortunately, there is no correlation between the translation key of the page title to the page-id.
Now, instead of creating a mapping between page-ids and page title translations that I will have to maintain by hand, I figured I could lookup the route in the router config and require the component:
{
path: 'the-page',
name: 'ThePage',
component: resolve => require.ensure([], () => resolve(require('#/pages/ParentPage/ChildPage/ThePage')), 'parent-page')
}
This works by recursively walking through the routes to find the page-id, then loading the component in a promise:
// find the route definition
const route = findRouteByPath(file.areaName)
// load the route component
new Promise(resolve => { route.component(resolve) })
.then(component => {
console.log(component)
})
This gives me the component like so:
Object {
mixins: Array(1),
staticRenderFns: Array(0), __file: "/the/file/path.vue",
beforeCreate: Array(1),
beforeDestroy: Array(1),
components: Object,
mixins: Array(1),
render: function (),
// etc.
}
If I call the render function, I get an error:
Uncaught (in promise) TypeError: Cannot read property '_c' of undefined
What I want to do is programmatically render the component and grab the content of the slot page-title from the component.
Can anyone help with this?
Update 1
Vue.extend() then mounting seems to do what I want. The problem is, I get all kinds of errors because the pages depend on certain things being around, like the current route. I'm also a bit worried to trigger certain created() hooks that would trigger API calls and such. My current state is trying to get rid of mixins and hooks like so:
const strippedComponent = Object.assign(component, {
mixins: [],
components: {},
beforeCreate: []
})
const Constructor = Vue.extend(strippedComponent)
const vm = new Constructor({})
But I'm not quite there yet.
Update 2
I ended up abandoning this and bit the bullet by adding a meta: {} object to each route where I store things like the page title translation key and other good stuff.

How to Properly Use Vue Router beforeRouteEnter or Watch to trigger method in Single File Component?

I'm working on an app in Vue.js using Single File Components and Vue Router. I have a Search component where I need to execute a method to re-populate search results each time a user visits the route. The method executes correctly the first time the route is visited because of the "create" hook:
created: function() {
this.initializeSearch();
},
However, when the user leaves the route (to register or log into the app for instance), and returns to the Search page, I can't seem to find a way to automatically trigger this.initializeSearch() on subsequent visits.
Routes are set up in index.js like so:
import Search from './components/Search.vue';
import Login from './components/Login.vue';
import Register from './components/Register.vue';
// Vue Router Setup
Vue.use(VueRouter)
const routes = [
{ path: '/', component: Search },
{ path: '/register', component: Register },
{ path: '/login', component: Login },
{ path: '*', redirect: '/' }
]
export const router = new VueRouter({
routes
})
I gather that I should be using "watch" or "beforeRouteEnter" but I can't seem to get either to work.
I tried using "watch" like so within my Search component:
watch: {
// Call the method again if the route changes
'$route': 'initializeSearch'
}
And I can't seem to find any documentation explaining how to properly use the beforeRouteEnter callback with a single file component (the vue-router documentation isn't very clear).
Any help on this would be much appreciated.
Since you want to re-populate search results each time a user visits the route.
You can use beforeRouteEnter() in your component as below:
beforeRouteEnter (to, from, next) {
next(vm => {
// access to component's instance using `vm` .
// this is done because this navigation guard is called before the component is created.
// clear your previously populated search results.
// re-populate search results
vm.initializeSearch();
})
}
You can read more about navigation guards here
Here is the working fiddle