Emit Events and On Vuejs 2 - vue.js

Hi this is my parent custom events
bus.$emit('updated-users', 'Sample Data')
My component
bus.$on('updated-users', data => this.lastname = data)
This one works.
Using Callback doesn't work.
bus.$on('updated-users', function(data){
//data is displayed in the console log.
console.log(data)
//This doesn't work.
this.fetchedUsersData()
})
I need to trigger a method but it doesn't work. I can't find any example that trigger a method. Please help.

Can you try this
created () {
bus.$on('updated-users', fetchedUsersData)
},
methods: {
fetchedUsersData(){
//the fetched user data
console.log('the fetched user data')
}
}

Related

Update data without refresh the page with Vue and axios

I have a page with 2 tabs (Questions and Data) made on Vue and Axios.
In first Tab, I fill the form and submit it - Save button v-on:click="save".
save: function() {
axios({
method: 'patch',
url: url,
data: this.data
})
.then(function (response) {
this.data = response.data;
}
In the second Tab(Data) I have the list of saved data:
mounted() {
axios
.get('/api/recommended-products/?patient_uuid=' + '{{patient.uuid}}')
.then(response => (this.data= response.data.results))
}
Now when I change answers in Questions Tab my list in Data Tab should change automatically. It changes if I refresh the page - mounted() works.
I tried to create updateList() function:
updateList: function() {
axios
.get('/api/recommended-products/?patient_uuid=' + '{{patient.uuid}}')
.then(response => (this.data= response.data.results))
}
and added it to save() function like:
save: function() {
axios({
method: 'patch',
url: url,
data: this.data
})
.then(function (response) {
this.data = response.data;
this.updateList();
}
The problem is that this way works other second time (sometime works sometimes not). So I just added location.reload(); to save() but I don't like this approach. Is it possible to update Data list without refreshing the page? What am I doing wrong with updateList() function?
In my opinion here you should use vuex and its getters.
You would then have only one request in all the application and the data would be automatically refreshed once updated in the state.
You can then access the data using a computed property which will be automatically re-rendered when the state is updated.
Here is an example using multiple tabs : https://codesandbox.io/s/vuex-axios-demo-forked-m0cqe4?file=/src/App.vue
In this example, i'm loading posts information through the JsonPlaceHolder API.
Every time the form is re send (using a function). The action of the store is called, then the state is updated which trigger the getter to re-render the data.
Note: i'm loading the first post into the mounted with a default value of 1 here.
save: function() {
axios({
method: 'patch',
url: url,
data: this.data
})
.then(function (response) {
this.data = […this.data, …response.data]
}
You have re rendered issue I think can can you try above solution
I think this might be helpful. Try to implement something like following.
async function() {
try{
await axios.post() // or any request
//action if success
//another action if success
...
} catch(error) {
//do something with error.
console.log(error)
}

VueJS: $router.push not working with query parameters

In my NuxtJS(v. 2.10.2) application, I have a URL like below where pid is a post's id.
/post?pid=5e4844e34202d6075e593062
This URL works fine and loads the post as per the value passed to the pid query parameter. However, user can add new post by clicking Add Post button on the application bar that opens a dialog. Once the user clicks add, a request to back-end server is made to save the request. And once successful, user is redirected to the new post using vue router push like below
.then(data => {
if (data) {
this.$router.push({ path: `/post?pid=${data.id}` });
}
})
The problem is, user is not redirected to the new post, only the query parameter pid is updated. I suspect VueJS does not acknowledge this as a different URL and hence does nothing.
How to fix this?
Update: As an alternative tried the syntax below but getting the same behavior.
this.$router.push({ path: "post", query: { pid: data.id } });
Say you have a component post.vue which is mapped with /post URL.
Now if you redirect the user to /post?pid=13, the post.vue component won't mount again if it's already mounted ie. when you are already at /post or /post?pid=12.
[1] In this case, you can put a watch on the route to know if the route has been changed.
watch: {
'$route.path': {
handler (oldUrl, newUrl) {
let PID = this.$route.query.pid
// fetch data for this PID from the server.
// ...
}
}
}
OR
[2] If the component post.vue is mapped with some route say /post.
You can also use the lifecycle -> beforeRouteUpdate provided by vue-router
beforeRouteUpdate (to, from, next) {
let PID = to.query.pid
// fetch data for this PID from the server.
// ...
next()
}
By changing the approach component data can be updated as per the new query string value. Here is how it can be done.
Rather than trying to push to the same page again with different query string. The query string pid itself can be watched for change and on update new data can be fetched and the component data can be updated. In NuxtJS(v. 2.10.2) apps, this can be achieved with watchQuery. watchQuery is a NuxtJS property which watches changes to a query strings. And once it detects the change, all component methods(asyncData, fetch, validate..) are called. You can read more https://nuxtjs.org/api/pages-watchquery/
As for the solution, pushing to the same page with new query string remains the same.
.then(data => {
if (data) {
this.$router.push({ name: 'post', query: { pid: data.id } });
}
})
However, on the page.vue, where the data is fetched from the server. We need to add watchQuery property.
watchQuery: ["pid"],
async asyncData(context) {
let response = await context.$axios.$get(
`http://localhost:8080/find/id/${context.route.query.pid}`
);
return { postData: response };
},
data: () => ({
postData: null
})
Now, everytime the query string pid will change asyncData will be called. And that is it. An easy fix to updating component data when the query string value change.
try this solution
.then(data => {
if (data) {
this.$router.push({ name: 'post', query: { pid: data.id } });
}
})
hints:
// with query, resulting in /register?plan=private
router.push({ path: 'register', query: { plan: 'private' } })
Use watchQuery property (https://nuxtjs.org/docs/2.x/components-glossary/pages-watchquery)
export default {
watchQuery: true,
data: () => ...
}
In case anybody was looking for this:
Query parameters specified as a string do not work when passed to a path parameter:
router.push({path: 'route?query=params'})
When you want you use them as a string, just pass the whole string as an argument, like so: router.push('route?query=params')
It'll then be automagically picked by router and navigation will happen.
try this :
.then(data => {
if (data) {
this.$router.push('/post?pid=' + data.id);
}
})
hope it works!!!

Debounce Vuex Action Call to Database Not Working

I have a few components that can be separate or on the same page. Each of these components uses the same Vuex state. Since they can each be used on other pages and still work, each of them dispatches a call to the same Vuex action which in turns calls a service that uses axios to get the JSON data.
All of this works great!
However, when I do have 2 (or more) of these components on a single page, that axios call gets called 1 time for each of the components. Initially, I went down the path of trying to see if data existed and get created a "last got data at" timestamp so I could just bypass the 2nd call. However, these are happening both on the components created event and are being essentially called at the same time.
So, enter debounce. Seems like the exact reason for this. However, when I implement it, it fails and is passing on to the next line of code and not awaiting. What am I doing wrong?
Agenda Component (one that uses the same state)
async created() {
await this.gatherCalendarData();
},
methods: {
async gatherCalendarData() {
await this.$store.dispatch('time/dateSelected', this.$store.state.time.selectedDate);
},
},
Month Component (another, notice they are the same)
async created() {
await this.gatherCalendarData();
},
methods: {
async gatherCalendarData() {
await this.$store.dispatch('time/dateSelected', this.$store.state.time.selectedDate);
},
},
The Action getting called
async dateSelected(context, data) {
let result = await getCalendarData(isBetween.date, context.rootState.userId);
await context.commit('SET_MONTHLY_DATA', { result: result.Result, basedOn: isBetween.date });
},
This getCalendarData method is in a service file I created to make api calls (below.)
This is the error that I receive (once for each component) that calls this action.
[Vue warn]: Error in created hook (Promise/async): "TypeError: Cannot read property 'Result' of undefined"
Which is referring to the 3rd line above: result: result.Result
API Service
const getCalendarData = debounce(async (givenDate, userId) => {
let response = await getCalendarDataDebounced(givenDate, userId);
return response;
}, 100);
const getCalendarDataDebounced = async (givenDate, userId) => {
let result = await axiosGet('/api/v2/ProjectTime/BuildAndFillCalendarSQL', {
givenDate: givenDate,
userID: userId,
});
return result;
};
Axios Wrapper
const axiosGet = async (fullUrl, params) => {
let result = null;
try {
let response = await axios.get(fullUrl, params ? { params: params } : null);
result = await response.data;
} catch(error) {
console.error('error:', error);
}
return result;
};
If I put console.log messages before, after and inside the getCalendarData call as well as in the getCaledarDataDebounced methods: (assuming just 2 components on the page) the 2 before logs show up and then the 2 after logs appear. Next the error mentioned above for each of the 2 components, then a single 'inside the getCalendarData' is logged and finally the log from within the debounced version where it actually gets the data.
So it seems like the debouncing is working in that it is only run a single time. But it appears that await call let result = await getCalendarData(isBetween.date, context.rootState.userId); is not truly Waiting.
Am I missing something here?
EDITS after Answer
Based on #JakeHamTexas' answer, my action of dateSelected is now (actual full code, nothing removed like above as to not confuse anything):
async dateSelected(context, data) {
console.log('dateSelected action');
let isBetween = isDateWithinCurrentMonth(data, context.state);
if (!isBetween.result) {
// The date selected is in a different month, so grab that months data
return new Promise(resolve => {
getCalendarData(isBetween.date, context.rootState.userId)
.then(result => {
console.log('inside promise');
context.commit('SET_MONTHLY_DATA', { result: result.Result, basedOn: isBetween.date });
context.commit('SET_SELECTED_DATE', isBetween.date);
context.commit('statistics/TIME_ENTRIES_ALTERED', true, { root: true });
resolve();
});
});
} else {
// The date selected is within the given month, so simply select it
context.commit('SET_SELECTED_DATE', data);
}
context.commit('CLEAR_SELECTED_TIME_ENTRY_ID');
},
And my API call of getCalendarData is now:
const getCalendarData = async (givenDate, userId) => {
console.log('getting calendar data');
let result = await axiosGet('/api/v2/ProjectTime/BuildAndFillCalendarSQL', {
givenDate: givenDate,
userID: userId,
});
return result;
};
The error is gone! However, it does not seem to be debouncing - meaning everything gets called 3 times. I would expect the dateSelected action to be called 3 times. But I would like to avoid the getting calendar data being called 3 times. If it helps, this is what the console looks like:
dateSelected action
getting calendar data
dateSelected action
getting calendar data
dateSelected action
getting calendar data
inside promise
inside promise
inside promise
You need to return a promise from your action. Returning a promise of undefined (which is what is currently happening) resolves immediately.
dateSelected(context, data) {
return new Promise(resolve => {
getCalendarData(isBetween.date, context.rootState.userId)
.then(result => {
context.commit('SET_MONTHLY_DATA', { result: result.Result, basedOn: isBetween.date });
resolve();
}
}
},
Additionally, a vuex commit does not return a promise, so it doesn't make sense to await it.

Vue.js, VueX - cannot render data from API in a component

I want to make an API call to the server to fetch the data and then display them in a component. I have a created() method which dispatches an action to my store, which, in turn, commits the mutation to udpate my store with the data I got from the server. I also have computed method where I simply call the getter which fetches the data from the store. The code looks like this:
state
state: {
data: {
rides: []
}
}
component.vue
created() {
this.$store.dispatch('fetchUserRides');
}
computed: {
...mapGetters([
'userRides'
]),
}
store.js
//actions
fetchUserRides({ commit }) {
axios.get('/api/stats/current_week')
.then(response => {
commit('fetchUserRides', response)
})
.catch(error => {
commit('serverResponsError')
})
//mutations...
fetchUserRides(state, payload){
let rides = payload.data
rides.forEach((item) => {
state.data.rides.push(item)
})
//getters
userRides: state => {
let rides = state.data.rides
rides.sort(( a, b) => {
return new Date(a.date) - new Date(b.date);
});
return rides
}
I receive over 40 objects in the response, I did check it by console.log(state.data.rides) and they are there in 100%.
My problem is that when I log off and log back in again it throws an error "TypeError: Cannot read property 'sort' of null". But if I hit Refresh they appear fine. The login action redirects me to the page where I render this component. This looks like the computed property first tries to fetch data by the getter from the array before it is actually populated in the store. How can I make sure I get the array of objects in my component?
You probably need to set an empty array ([]) as an initial value to state.data.rides instead of null.
Another option will be to check that rides is truthy in your getters.
Something like:
if (rides) {
rides.sort(( a, b) => {
return new Date(a.date) - new Date(b.date);
});
}
return []
I was able to resolve my problem and it turns out I made a mistake. I completely forgot I set state.data.rides = null instead of an empty array state.data.rides = null, which would explain why the array was empty. It was a legacy code I had :)

Which Lifecycle hook after axios get but before DOM render

I'm trying to render my DOM, dependent on some data I'm returning from an axios get. I can't seem to get the timing right. The get is in the created hook, but there is a delay between the get and actually receiving the data. Basically if there is info in seller_id then I need to show the cancel button, otherwise don't. Here is my code:
this is in my created hook
axios.get('https://bc-ship.c9users.io/return_credentials').then(response => {
this.seller_id = response.data.seller_id;
this.selected_marketplace = response.data.marketplace;
this.token = response.data.auth_token;
});
and then this is the logic to show or hide the button. I've tried created, mounted, beforeUpdate, and updated all with no luck. I've also tried $nextTick but I can't get the timing correct. This is what I have currently:
beforeUpdate: function () {
// this.$nextTick(function () {
function sellerIdNotBlank() {
var valid = this.seller_id == '';
return !valid;
}
if(sellerIdNotBlank()){
this.show_cancel_button = true;
}
// })
},
First, it is pointless to get your data from backend and try to sync with Vue.js lifecycle methods. It never works.
Also, you should avoid beforeUpdate lifecycle event. It is often a code smell. beforeUpdate is to be used only when you have some DOM manipulations done manually and you need to adjust them again before Vue.js attempt to re-render.
Further, show_cancel_button is a very good candidate for a computed property. Here is how component will look:
const componentOpts = {
data() {
return {
seller_id: '',
// ... some more fields
};
},
created() {
axios.get('https://bc-ship.c9users.io/return_credentials').then(response => {
this.seller_id = response.data.seller_id;
this.selected_marketplace = response.data.marketplace;
this.token = response.data.auth_token;
});
},
computed: {
show_cancel_button() {
return this.seller_id !== '';
}
}
}