Why do I receive Promise {<pending>} with Vuejs - vue.js

I have a very simple two methods in Vuejs and it returns me promise pending what I am doing it's this:
TotalIncomeData(day, branch_office_id) {
return fetch('/api/collection/total/'+day+'/'+branch_office_id+'?api_token='+App.apiToken)
.then(response => {
return response.data;
}
);
},
getTotalIncomes(day, branch_office_id) {
console.log(this.TotalIncomeData(day, branch_office_id))
},
Why am I not getting the value? Thanks

You need to call .json() on the fetch response, and await the result.
And you need to make your function async and await the promise from the first function.
TotalIncomeData(day, branch_office_id) {
return fetch(...).then(rawResponse => rawResponse.json())
^^^^^^
},
async getTotalIncomes(day, branch_office_id) {
^^^^^
console.log(await this.TotalIncomeData(day, branch_office_id))
^^^^^
},

Related

Vuejs created and mounted doesn't work properly even if at the same level than methods

I'm experiencing a strange behaviour with created() and mounted() in Vue.js. I need to set 2 lists in created() - so it means those 2 lists will help me to create a third list which is a merge.
Here is the code :
// return data
created () {
this.retrieveSellOffers();
this.getAllProducts();
},
mounted () {
this.mergeSellOffersProducts();
},
methods: {
retrieveSellOffers() {
this.sellerId = localStorage.sellerId;
SellOfferServices.getAllBySellerId(this.sellerId)
.then((response) => {
this.sellOffers = response.data;
console.log("this.sellOffers");
console.log(this.sellOffers);
})
.catch((e) => {
console.log(e);
});
},
getAllProducts() {
ProductServices.getAll()
.then((response) => {
this.products = response.data;
console.log("this.products");
console.log(this.products);
})
.catch((e) => {
console.log(e);
});
},
mergeSellOffersProducts () {
console.log(this.products) // print empty array
console.log(this.sellOffers) // print empty array
for (var i = 0; i < this.sellOffers.length; i++) {
if (this.sellOffers[i].productId === this.products[i]._id) {
this.arr3.push({id: this.sellOffers[i]._id, price: this.sellOffers[i].price, description: this.products[i].description});
}
}
this.arr3 = this.sellOffers;
},
}
//end of code
So my problem is when I enter in mergeSellOffersProducts(), my 2 lists are empty arrays :/
EDIT :
This way worked for me :
async mounted() {
await this.retrieveSellOffers();
await this.getAllProducts();
this.mergeSellOffersProducts();
},
methods: {
async retrieveSellOffers() {
this.sellerId = localStorage.sellerId;
this.sellOffers = (await axios.get('link/api/selloffer/seller/', { params: { sellerId: this.sellerId } })).data;
},
async getAllProducts() {
this.products = (await axios.get('link/api/product')).data;
},
}
I think the reason is: Vue does not wait for the promises to resolve before continuing with the component lifecycle.
Your functions retrieveSellOffers() and getAllProducts() contain Promise so maybe you have to await them in the created() hook:
async created: {
await this.retrieveSellOffers();
await this.getAllProducts();
}
So I tried to async my 2 methods :
async retrieveSellOffers() {
this.sellerId = localStorage.sellerId;
this.sellOffers = (await axios.get('linkhidden/api/selloffer/', { params: { sellerId: '615b1575fde0190ad80c3410' } })).data;
console.log("this.sellOffers")
console.log(this.sellOffers)
},
async getAllProducts() {
this.products = (await axios.get('linkhidden/api/product')).data;
console.log("this.products")
console.log(this.products)
},
mergeSellOffersProducts () {
console.log("here")
console.log(this.sellOffers)
console.log(this.products)
this.arr3 = this.sellOffers;
},
My data are well retrieved, but yet when I enter in created, the two lists are empty...
You are calling a bunch of asynchronous methods and don't properly wait for them to finish, that's why your data is not set in mounted. Since Vue does not await its lifecycle hooks, you have to deal with the synchronization yourself.
One Vue-ish way to fix it be to replace your method mergeSellOffersProducts with a computed prop (eg mergedSellOffersProducts). Instead of generating arr3 it would simply return the merged array. It will be automatically updated when products or sellOffers is changed. You would simply use mergedSellOffersProducts in your template, instead of your current arr3.
If you only want to update the merged list when both API calls have completed, you can either manually sync them with Promise.all, or you could handle this case in the computed prop and return [] if either of the arrays is not set yet.
When you're trying to merge the 2 lists, they aren't filled up yet. You need to await the calls.
async created () {
await this.retrieveSellOffers();
await this.getAllProducts();
},
async mounted () {
await this.mergeSellOffersProducts();
},

Access component's this inside firebase promise reposonse

I've tried to bind it like it doesn't seem to make the trick :)
firebaseInstance.auth().fetchSignInMethodsForEmail(this.signUpData.email)
.then((response) => {
... all logic
}).bind(this)
...since it outputs the following error:
firebaseInstance.auth(...).fetchSignInMethodsForEmail(...).bind is not a function
Here is the component's logic, can someone please suggest a proper way to access this after firebase response resolves? :bowing:
import { VALIDATION_MESSAGES, VALUES } from './signup.module.config'
import GLOBAL_EVENTS from 'values/events'
import { firebaseInstance } from 'database'
export default {
name: `SignUpForm`,
data() {
return {
signUpData: {
email: ``,
password: ``,
confirmPassword: ``
}
}
},
methods: {
onEmailSignUp() {
// Here this is component
console.log(this.$refs)
firebaseInstance.auth().fetchSignInMethodsForEmail(this.signUpData.email)
.then((response) => {
// other logic
} else {
// Here this is lost and equals undefined
this.$refs.email.setCustomValidity(`error`)
}
})
}
}
}
The bind instruction should be used on a function object, not on a function return value.
By doing
firebaseInstance.auth().fetchSignInMethodsForEmail(this.signUpData.email)
.then((response) => {
... all logic
}).bind(this)
You try to use bind on the return of the then method of you promise, which is a promise object and can't use bind.
You can try firebaseInstance.auth().fetchSignInMethodsForEmail(this.signUpData.email)
.then(function(response){
... all logic
}.bind(this))
instead. Here the bind is put on the function send in the promise so it should work correctly. I also transformed the function from arrow function to normal, because I think there is no need for arrow function with bind.
Using ES8 async/await sugar syntax you can do it like this :
async onEmailSignUp () {
try {
const response = await firebaseInstance.auth().fetchSignInMethodsForEmail(this.signUpData.email)
// other logic
} catch (error) {
console.log(error)
this.$refs.email.setCustomValidity(`error`)
}
}

Can't use Vue.js Data in Created ()

I'm wondering if is it possible, how can I use Vue.js data in my Created() function.
I'll show some code so you can see why I say.
data (){
return {
model: {},
foo: 'boo'
}
},
created (){
const getModel = () => {
const modelId = this.$route.params.id
axios.get('/api/model', { params: {modelId: modelId}})
.then(res => {
this.model = res.data
this.boo = 'hello'
console.log(this.model)
console.log(this.foo)
})
.catch(err => console.log(err))
}
getModel()
const init = () =>{
console.log(this.model)
console.log(this.foo)
}
init()
The first console.log(foo) returns 'hello'.
The second one (init) returns 'boo'.
Also the first console.log(this.model) is what I expect to get but once is out of the axios method it's like empty again all over the mounted function.
I've tried a lot of things but none of them worked, hope I get a solution... Thanks in advance!
As soon as JS functions are non-blocking - your axios call isn't done (model is still empty) when you call for init
Define init as components method
Call this.init() in axios.get callback
It might have to do with the fact that in your created hook you're creating a function using the function keyword, which means your init function will have its own context(its own this).
A solution to this problem would be to use an arrow function.
data () { return { foo: 'bar' } }
created () {
const init = () => {
console.log(this.foo);
}
init(); // bar
}
More about arrow functions
UPDATE
Actually, the issue stems from not awaiting for getModel. Because you are making a request, you first need to wait for the promise to resolve, and then use its resolved data in the code that depends on it.
The async/await version would be:
async created () {
const getModel = async () => {
const modelId = this.$route.params.id
try {
const res = await axios.get('/api/model', { params: {modelId: modelId}})
this.model = res.data
this.boo = 'hello'
console.log(this.model)
console.log(this.foo)
} catch (err) {
console.error(err)
}
}
const init = () =>{
console.log(this.model)
console.log(this.foo)
}
// An async function always returns a promise
await getModel();
init();
}

How Can I pass params with an API client to vue-head?

I am passing params from my API to vue-head but every time I do that it send me undefined in the head this is the code:
export default {
data: () => ({
errors: [],
programs: [],
}),
methods: {
getProgram() {
this.api.http.get(`videos/program/${this.programSlug}`)
.then(response => {
this.programs = response.data
})
.catch(error => {
this.errors = error
});
}
},
head: {
title: function() {
return {
inner: this.programs.name,
separator: '|',
complement: 'Canal 10'
};
}
}
}
any idea what I am doing wrong with my code??
First verify you are fetching the information correctly. Use console log and go to network tab and verify you are fetching the data correct, you might have to comment out vue-head. But what I think is that the problem might be due to vue-head rendering before the api call finishes then no data is being passed.
If you are using vue-router this can be easily solved with beforeRouteEnter() hook. But if not! apparently vue-head has an event that you can emit to update the component after render.
I haven't tried this but it should work. you can add the function below to your methods and call it after the promise is resolved i.e in the then closure.
methods: {
getProgram() {
this.api.http.get(`videos/program/${this.programSlug}`)
.then(response => {
this.programs = response.data
this.$emit('updateHead')
})
.catch(error => {
this.errors = error
});
}
}

VueRouter - Fetching Before Navigation - Multiple AJAX

I would like to use the beforeRouteEnter guard so I can be sure my data is loaded before going to a page. I read the example you can find here in the vue-router documentation.
Current situation
I'm more or executing two AJAX calls to get some data in the created lifecycle event.
export default {
created() {
const _this = this;
axios.get('/getCompanyDetails').then((response) => {
_this.private.company_details = response.data
});
axios.get('/getusers').then((response) => {
if(response.data){
_this.private.company_users = response.data;
}
});
}
}
What I try
beforeRouteEnter (to, from, next) {
function getCompanyDetails() {
return axios.get('/getCompanyDetails')
}
function getUsers() {
return axios.get('/getusers');
}
axios.all([getCompanyDetails(), getUsers()])
.then(axios.spread(function (company_details, company_users) {
next(vm => vm.setData(err, company_details, company_users))
}));
},
Am I on the right track ? The only thing I see here is I fell I'm required to call only one function setData in the next with all the parameters received from the different AJAX calls.
Is there a way to call several functions like setUsers(), setDetails() in the next ?
Is there a better way than what I'm doing ?
As #thanksd stated :
next((vm) => { vm.setUser(err, company_users); vm.setDetails(err, company_details); })
The final answer is then
beforeRouteEnter (to, from, next) {
function getCompanyDetails() {
return axios.get('/getCompanyDetails')
}
function getUsers() {
return axios.get('/getusers');
}
axios.all([getCompanyDetails(), getUsers()])
.then(axios.spread(function (company_details, company_users) {
next((vm) => { vm.setUser(err, company_users); vm.setDetails(err, company_details); })
}));
},