VueJS router.push not updating data inside page - vue.js

I am using NuxtJS and I have a NavBar that goes to /website?web=1 and /website?web=2.. When I go from /website?web=1 to /website?web=2 vice versa.. My async fetch is not running at all.
website.vue
async fetch({ store, error, route, params }) {
let parameters;
const pageLimit = store.state.websites.pageLimit;
const offset = params.id ? (params.id - 1) * pageLimit : 0;
const web = route.query.web;
try {
if (web === "1") {
parameters = `?&is_global=true&offset=${offset}&limit=${pageLimit}`;
} else if (web === "2") {
parameters = `?&is_global=false&offset=${offset}&limit=${pageLimit}`;
} else {
parameters = `?co_id=${
route.query.co_id ? route.query.co_id : ""
}&ca_id=${
route.query.ca_id ? route.query.ca_id : ""
}&limit=${pageLimit}&offset=${offset}`;
}
await Promise.all([
store.dispatch("websites/fetchWebsites", parameters)
]);
} catch (e) {
console.log("Error: " + e);
}
},
NavBar.vue
methods: {
handleClick(tab, event) {
switch (tab.label) {
case "1":
this.$router.push({ name: "index" });
break;
case "2":
this.$router.push("/country");
break;
case "3":
this.$router.push("/website?web=1");
break;
case "4":
this.$router.push("/website?web=2");
break;
}
}
}

async fetch lifecycle is not invoked of query / param update
Sometimes you just want to fetch data and pre-render it on the server
without using a store. asyncData is called every time before loading
the page component. It will be called server-side once (on the first
request to the Nuxt app) and client-side when navigating to further
routes doc.
Also, a component does not remount on query / param update, so
lifecycles like created / mounted / beforeCreate etc are also not
invoked again. This helps in the application's performance as it avoids unnecessary rendering of the entire page where a few data changes would work.
Make a common method
methods: {
fetchData ({ store, error, route, params }) {
// your fetch logic here
let parameters;
const pageLimit = store.state.websites.pageLimit;
// ...
}
}
Call the method in async data
async fetch({ store, error, route, params }) {
this.fetchData({ store, error, route, params })
}
Call the method again on query change
watch: {
"$route.query.web": {
handler () {
this.fetchData({ store: this.$store, route: this.$route... });
}
},
Alternative to watch
beforeRouteUpdate (to, from, next) {
if (from.name === to.name) { // Call fn only when: route hasn't changed instead just query / params for the route has changed
this.fetchData({ store: this.$store, route: this.$route... })
}
},

When using Nuxt's fetch(), you need an explicit watcher to listen for route changes.
For a Nuxt component which has async fetch(), if you want it to update when the route changes, then setup a standard watcher.
See docs: https://nuxtjs.org/docs/2.x/features/data-fetching#listening-to-query-string-changes
export default {
watch: {
'$route.query': '$fetch' // This runs $fetch, defined below
},
async fetch() {
// Runs on server load (SSR), or when called (see above)
}
}
For other context's (or before Nuxt 2.12):
You could explore using watchQuery.
See docs: https://nuxtjs.org/docs/2.x/components-glossary/pages-watchquery/
export default {
watchQuery(newQuery, oldQuery) {
// Only execute component methods if the old query string contained `bar`
// and the new query string contains `foo`
return newQuery.foo && oldQuery.bar
}
}
https://nuxtjs.org/docs/2.x/components-glossary/pages-watchquery/

Related

Using XState in Nuxt 3 with asynchronous functions

I am using XState as a state manager for a website I build in Nuxt 3.
Upon loading some states I am using some asynchronous functions outside of the state manager. This looks something like this:
import { createMachine, assign } from "xstate"
// async function
async function fetchData() {
const result = await otherThings()
return result
}
export const myMachine = createMachine({
id : 'machine',
initial: 'loading',
states: {
loading: {
invoke: {
src: async () =>
{
const result = await fetchData()
return new Promise((resolve, reject) => {
if(account != undefined){
resolve('account connected')
}else {
reject('no account connected')
}
})
},
onDone: [ target: 'otherState' ],
onError: [ target: 'loading' ]
}
}
// more stuff ...
}
})
I want to use this state machine over multiple components in Nuxt 3. So I declared it in the index page and then passed the state to the other components to work with it. Like this:
<template>
<OtherStuff :state="state" :send="send"/>
</template>
<script>
import { myMachine } from './states'
import { useMachine } from "#xstate/vue"
export default {
setup(){
const { state, send } = useMachine(myMachine)
return {state, send}
}
}
</script>
And this worked fine in the beginning. But now that I have added asynchronous functions I ran into the following problem. The states in the different components get out of sync. While they are progressing as intended in the index page (going from 'loading' to 'otherState') they just get stuck in 'loading' in the other component. And not in a loop, they simply do not progress.
How can I make sure that the states are synced in all my components?

Gridsome mounted method is only running on page reload

I am using the vue mounted lifecyle method to fetch data. The data is stored in algolia. I use the search api to connect and fetch it. The data is only loaded when I refresh the site. It does not run on page navigation.
methods: {
async fetchInventory(data = {}) {
try {
this.isLoading = true;
const result = await index.search("", {hitsPerPage: 12});
this.auctions = result.hits;
this.totalItems = result.nbHits;
this.totalPages = result.nbPages;
this.isLoading = false;
} catch (error) {
this.isLoading = false;
console.log(error);
}
},
},
mounted() {
this.fetchInventory();
}
If this is client side rendering you may need to wait until nextTick OR use earlier/later hook:
mounted() {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been rendered
})
}
May need to use beforeCreate or created hook if rendering serverside.
Also how is page navigation being done? if you're using navigation API or a library that may be critical to fixing the issue

Nuxt watch does not redirect

I have a Nuxt application with profile page. This page has a watcher which checks store.state.auth.isAuthenticated value. If it is false watcher should redirect to login page. The weird is that although the condition is evaluated right it does not redirect to login.
watch: {
'$store.state.auth.isAuthenticated': {
imediate: true,
deep: false,
handler(newVal) {
if( !newVal ) this.$router.push({'name': 'login'});
}
},
},
As I wrote above, condition is evaluated right but it does not trigger $router.push(). I dont understand it. What is wrong with that code?
EDIT: It creates the endless loop in auth.js middleware.
import { createNavigationGuard } from "~/plugins/navigation-guard.js";
export default function (context) {
if (process.client) {
const watchedStores = [];
const unwatchStore = (unwatch) => {
if (typeof unwatch === "function") {
unwatch();
}
};
// TODO: Find out whether the watchers persist after each route
// Unwatch previous route - this could be necessary for performance
console.log('watchedStores');
console.log(watchedStores);
unwatchStore(watchedStores.pop());
const unwatch = context.store.watch(
(state) => {
return state.auth.isAuthenticated;
},
(isAuthenticated) => {
createNavigationGuard(context, isAuthenticated);
},
{
immediate: true,
}
);
// it's not necessary to reassign unwatched variable to undefined with array
watchedStores.push(unwatch);
}
if (process.server) {
createNavigationGuard(
context,
context.store.state.auth.isAuthenticated
);
}
}
have you tried to make a method for the redirect and just call that method in your watch handler?
so instead of this.$router.push do this.redirectUser()
and in the method 'redirectUser()' do:
this.$router.push({'name': 'login'})

Returning Apollo useQuery result from inside a function in Vue 3 composition api

I'm having some issues finding a clean way of returning results from inside a method to my template using Apollo v4 and Vue 3 composition API.
Here's my component:
export default {
components: {
AssetCreationForm,
MainLayout,
HeaderLinks,
LoadingButton,
DialogModal
},
setup() {
const showNewAssetModal = ref(false);
const onSubmitAsset = (asset) => {
// how do I access result outside the handler function
const { result } = useQuery(gql`
query getAssets {
assets {
id
name
symbol
slug
logo
}
}
`)
};
}
return {
showNewAssetModal,
onSubmitAsset,
}
},
}
The onSubmitAsset is called when user clicks on a button on the page.
How do I return useQuery result from the setup function to be able to access it in the template? (I don't want to copy the value)
You can move the useQuery() outside of the submit method, as shown in the docs. And if you'd like to defer the query fetching until the submit method is called, you can disable the auto-start by passing enabled:false as an option (3rd argument of useQuery):
export default {
setup() {
const fetchEnabled = ref(false)
const { result } = useQuery(gql`...`, null, { enabled: fetchEnabled })
const onSubmitAsset = (asset) => {
fetchEnabled.value = true
}
return { result, onSubmitAsset }
}
}
demo

How to subscribe mutation changes of a store with multiple modules?

Using vue.js to build a login page. In my project, I have splited my store into two modules (User、Info).
In the User module, the actions.js aim to handle some asynchronous requests (such as login、register), and commit correspond mutation.
export const userActions = {
login({commit}, loginUser) {
commit(LOGIN)
axios.post(`${ API_BASE_USER }/login`, loginUser)
.then(res => {
const { token } = res.data
if (res.status == 200) { commit(LOGIN_SUCCESS, token) }
else { commit(LOGIN_FAILURE, res.data) }
})
}
.......
}
I knew that: In Vuex, we can subscribe store mutations.
I want to subuscribe every mutation changes in Login.vue
so I can load a notification to tell user login successully or not.
Login.vue
created () {
this.$store.subscribe(mutation => {
switch (mutation.type) {
case LOGIN_SUCCESS:
console.log('view success')
// load success nitification
break
case LOGIN_FAILURE:
console.log('view failure')
// load success nitification
break
case LOGIN_WARNING:
console.log('view warning')
break
}
})
}
But this seems doesn't work.
Is it impossible to subscribe specific module's mutations in a store which have mutiple modules ?
Since you use the namespace for the store modules, you need to consider it when subscribing. For example:
created () {
this.$store.subscribe(mutation => {
switch (mutation.type) {
case MODULE_NAME + '/' + LOGIN_SUCCESS:
console.log('view success')
// load success nitification
break
...
}
})
}