Vue component doesn't update on route URL parameter change - vue.js

So I have a component which executes code once it's mounted like this:
mounted(){
axios.get('/markers/' + this.username)
.then(response => {
this.markers = response.data.markers
}).catch((error) => console.log(error));
}
And I get the username like this:
username: this.$route.params.username
however, if I change the URL parameter, the username doesn't update so my AXIOS call doesn't update my markers. Why is this happening?

The reason is simple, even thought the URL is changing the component is not, VueJS is basically reusing the component and therefore not calling the mounted() method again.
Usually you can just setup a watcher and refactor a bit your code
methods: {
fetchData(userName) {
axios.get('/markers/' + this.username)
.then(response => {
this.markers = response.data.markers
}).catch((error) => console.log(error));
}
},
watch: {
'$route.params': {
handler(newValue) {
const { userName } = newValue
this.fetchData(userName)
},
immediate: true,
}
}
EDIT: Added the immediate true option and removed the mounted() hook

Related

Unabled to use Vuex Getter with params

I have Vuex Store that will look like this
const config = {
featureA: { isEnabled: true, maxUser: 2 },
featureB: { isEnabled: false, maxData: 5 },
}
const actions = {
getDataCompany(context, payload) {
return new Promise(async (resolve, reject) => {
try {
const result = await firebase.firestore().collection(payload.collection).doc(payload.companyId).get()
if (result) {
if (payload.isLogin) await context.commit('setConfig', result.data())
return resolve(result.data())
}
reject(new Error('Fail To Load'))
} catch (e) {
reject(new Error('Connection Error'))
}
})
}
}
const mutations = {
setConfig(state, payload) {
state.config = payload
}
}
const getters = {
getData: ({ config }) => (feature, key) => {
const state = config
if (state) if (state[feature]) if (state[feature][key]) return state[feature][key]
return null
}
}
export default new Vuex.Store({
state: { config },
actions: { ...actions },
mutations: { ...mutations },
getters: { ...getters }
})
It's working fine with this method to get the data
computed: {
featureAEnabled() {
return this.$store.getters.getData('featureA', 'isEnabled')
},
}
But I have a problem when the data is change, the value is not update in component, and now I want to use mapGetters because it say can detect changes, But I have problem with the documentation and cannot find how to pass params here,
import { mapGetters } from 'vuex'
computed: {
...mapGetters({
featureAEnabled: 'getData'
})
}
I'am calling the action from here
async beforeMount() {
await this.$store.dispatch('getDataCompany', {collection: 'faturelsit', companyId: 'asep', isLogin: true})
}
And try to detect change in here
mounted() {
if (this.featureAEnabled) console.log('feature enabled')
}
The value change is not detected, and need to refresh twice before the changes is implemented in component
My main target is to detect if there any data change in Vuex and make action in component,
nevermind just working with watch without mapgetter,
I just realize that computed cannot re-run the mounted, so I make method that will called when the variable change in watch. thank you.
The main purpose is fulfilled, but the mapgetter with params is still not answered. so if anyone want to answer please share the way to use mapgetter with params.
You could try to use get and set methods for your computed property.
Example:
computed: {
featureAEnabled: {
get() {
return this.$store.getters.getData('featureA', 'isEnabled')
},
set(value) {
...update featureEnabled property in vuex store
}
},
}

blink store data after change route

how can i avoid the data blink after update store data?
you can see the effect here:
https://drive.google.com/file/d/178raL6AJiC4bpIOImnaTKh6Yf9GruTCz/view?usp=sharing
component:
[...]
mounted() {
this.getIdeasFromBoard(this.$route.params.board_id);
},
[...]
store:
[...]
const actions = {
getIdeasFromBoard({ commit, dispatch }, board_id) {
apiClient
.get('/ideas/' + board_id)
.then((result) => {
console.log('success');
commit("SET_IDEAS_BOARD", result.data);
})
.catch(error => {
console.log('error' + error);
alert("You have failed to log in. Try again with another credentials.");
dispatch('auth/logout', null, { root: true });
this.$router.push({ name: "public" });
});
},
[...]
i've searched some simple tutorial about consuming api with error handling, but didnt find it.
thanks
It's because IDEAS_BOARD has the previous data until the new API call is completed. You would need to display a loader or a blank screen until the API call for the selected board is completed.
From actions, return a promise so that your component knows when is the call completed.
getIdeasFromBoard({ commit, dispatch }, board_id) {
return new Promise((resolve, reject) => {
apiClient
.get('/ideas/' + board_id)
.then((result) => {
console.log('success');
commit("SET_IDEAS_BOARD", result.data);
resolve()
})
.catch(error => {
console.log('error' + error);
alert("You have failed to log in. Try again with another credentials.");
dispatch('auth/logout', null, { root: true });
this.$router.push({ name: "public" });
reject()
});
})
},
In your .vue component,
async mounted () {
this.loading = true // some flag to display a loader instead of data
await this.$store.dispatch()
this.loading = false
}
There must be some other ways too like having this loading flag in the Vuex store. But it depends on you

how to call a function in vue when navigating way from route?

I am trying to call a function that clears local storage when the user navigates away from the current page. How would I accomplish this? I tried using the destroyed() lifecycle hook but it did not work. Would using beforeRouteLeave() be a good solution and how would I implement that in my routes file?
my route:
{
path: "/success",
name: "Success",
component: () =>
import("../views/Success.vue"),
},
my hook on the success page as it currently stands:
destroyed() {
window.localStorage.removeItem("intent");
},
what i tried with beforeRouteLeave
beforeRouteLeave: function(to, from, next) {
window.localStorage.removeItem("intent");
next();
},
my mounted hook
let intent = window.localStorage.getItem(intent);
// const product = window.localStorage.getItem(product);
axios
.get("http://localhost:5000/pay/confirm", {
params: {
intent: intent
}
})
.then(res => {
console.log(res.data.status);
if (res.data.status == "succeeded") {
console.log(res.data.status);
this.confirmPayment();
} else {
this.paid = false;
}
console.log(this.item);
});
},
You can try set (global/page) watcher for you, which will be do something when you route object changed
watch:{
$route function(to, from){
// do something
}
}
mayby you can use the beforeDestroy() hook instead of the destroyed() hook.

How to pass props parameter with axios in Vuejs?

I am passing a property in another component via props:
props: ['product']
This Product has some properties like: id, name etc.
Now, I want to get the category of this product with id via axios.
Is it possible? How?
My try:
mounted () {
axios
.get('/categoryofaproduct/', {
params: {
id: this.product.id
}
})
.then(response => {
console.log(response)
})
}
I think Props properties will not be accessable directly like this.
data() {
return {
id: null,
categoryName: null
}
}
In mounted you can copy the id like this and then it will work.
mounted () {
this.id = this.product.id
axios.get('/categoryofaproduct/'+this.id)
.then(response => {
this.categoryName = response.data
})
}
I would recommend to use back ticks to make it a string literal and interpolation to insert the values in. This just makes it more readable when you have multiple parameters in a URL
mounted () {
axios
.get(`/categoryofaproduct/${this.product.id}`
)
.then(response => {
console.log(response)
})
}
Then accept that parameter on your route
{
// create route with id as a parameter
path: "/categoryofaproduct/:id",
name: "category-of-a-product",
component: () => import("./views/categoryofaproduct.vue"),
},
To get access to that param create a computed field that returns it
computed: {
paramsId() {
return this.$route.params.id
},
}

Call Vuex action from component after mutation is complete

I have a Vue component that calls a Vuex action in its create hook (an api.get that fetches some data, and then dispatches a mutation). After that mutation is completed, I need to call an action in a different store, depending on what has been set in my store's state... in this case, getUserSpecials.
I tried to use .then() on my action, but that mutation had not yet completed, even though the api.get Promise had resolved, so the store state I needed to check was not yet available.
Does anyone know if there is a 'best practice' for doing this? I also considered using a watcher on the store state.
In my component, I have:
created () {
this.getUserModules();
if (this.userModules.promos.find((p) => p.type === 'specials')) {
this.getUserSpecials();
}
},
methods: {
...mapActions('userProfile', ['getUserModules',],),
...mapActions('userPromos', ['getUserSpecials',],),
},
In my store I have:
const actions = {
getUserModules ({ commit, dispatch, }) {
api.get(/user/modules).then((response) => {
commit('setUserModules', response);
});
},
};
export const mutations = {
setUserModules (state, response) {
Object.assign(state, response);
},
};
Right now, the simple if check in my create hook works fine, but I'm wondering if there is a more elegant way to do this.
[1] Your action should return a promise
getUserModules ({ commit, dispatch, }) {
return api.get(/user/modules).then((response) => {
commit('setUserModules', response);
})
}
[2] Call another dispatch when the first one has been resolved
created () {
this.getUserModules().then(response => {
this.getUserSpecials()
})
}
Make your action return a promise:
Change:
getUserModules ({ commit, dispatch, }) {
api.get(/user/modules).then((response) => {
commit('setUserModules', response);
});
},
To:
getUserModules({commit, dispatch}) {
return new Promise((resolve, reject) => {
api.get(/user/modules).then((response) => {
commit('setUserModules', response);
resolve(response)
}).catch((error) {
reject(error)
});
});
},
And then your created() hook can be:
created () {
this.getUserModules().then((response) => {
if(response.data.promos.find((p) => p.type === 'specials'))
this.getUserSpecials();
}).catch((error){
//error
});
},