VueJS Promise is failing - vue.js

I have a products component and a product owner component. Each Product will have an owner
What I am trying to do
I am receiving a list of products by calling an API endpoint. When the promise is resolved, I have a list of Products. Each Product has an OwnerID. I am trying to call another API Endpoint to fetch the name of the owner and assign it to the current product being iterated.
My Code so far
<script>
var config = require('../config');
export default {
data () {
return {
products: [],
}
},
ready () {
this.getProducts().then(t => {
console.log(t);
});
},
methods : {
getProducts : function() {
let url = config.API.GetProduct
this.$http.get(url).then(response=> {
this.products = response.data.resource;
var p = this.products.map(this.getOwner);
return Promise.all(p);
}, error=> {
console.error("An error happened!")
});
},
getOwner : function(product) {
let url = config.API.GetProductOwnerName.replace('[$$$]', product.OwnerID);
var p = new Promise();
this.$http.get(url).then(response => {
product.OwnerID = response.data.resource[0].OwnerName;
p.resolve(currentObj);
});
return p;
}
}
components: {}
}
</script>
Error that I am facing
Now whenever I am trying to do that, I keep getting the following errors
Uncaught TypeError: Cannot read property 'then' of undefined
Uncaught (in promise) TypeError: Promise resolver undefined is not a function(…)
Can somebody please let me know what I am doing wrong here ?
Thanks

You don't have to recreate a new promise object. You can just return the object you want to be passed to the next call.
getProducts: function() {
let url = config.API.GetProduct
return this.$http.get(url).then(response => {
this.products = response.data.resource;
return this.products.map(this.getOwner);
}, error=> {
console.error("An error happened!")
});
},

Related

Cannot read properties of undefined (reading 'elements') at VueComponent.mounted vue-stripe

i've a vue app which uses stripe as payment but each time i refresh i get
Cannot read properties of undefined (reading 'elements')
this the error i get in my console
Error in mounted hook: "TypeError: Cannot read properties of undefined (reading 'elements')"
this is my script tag on how i call my stripe payment
<script>
export default {
data() {
return {
error: "",
stripe: null,
card: null
};
},
computed: {
...mapGetters([
"getCart",
])
},
mounted() {
this.stripe = Stripe("pk_test_51KGqWkHC");
let elements = this.stripe.elements();
this.card = elements.create("card");
this.card.mount(this.$refs.card);
},
methods: {
async onPurchase() {
try {
let token = stripe.createToken(this.card);
let response = axios.post("http://localhost:5000/api/pay", {
token: token,
totalPrice: this.getCartTotalPriceWithShipping,
cart: this.getCart,
estimatedDelivery: this.getEstimatedDelivery
});
if (response.success) {
// Do something like redirecting to the home page
this.$store.commit("clearCart");
this.$router.push("/");
}
} catch (err) {
console.log(err);
}
},
}
};
</script>
I use this in my nuxt js project and it works fine,
please what i'm i doing wrong in vue
this.stripe.elements();
returns undefined, probably this is not working:
Stripe("pk_test_51KGqWkHC");
check that the initialization string you are using is correct.
Otherwise, check the docs.
if its undefined, you can handle this error:
mounted() {
this.stripe = Stripe("pk_test_51KGqWkHC");
if(this.stripe.elements) {
let elements = this.stripe.elements();
this.card = elements.create("card");
this.card.mount(this.$refs.card);
}
},
But seeing your whole code, you have some inconsistencies:
Im not sure how you're supposed to access to Stripe(), if you don't have imported it. Maybe it's a module?
Stripe("pk_test_51KGqWkHC") -> this.$stripe("pk_test_51KGqWkHC")
Then in let token = stripe.createToken(this.card);
stripe doesn't exists in async onPurchase(), so how do you have access to it?
This should be this.stripe.createToken(this.card) or this.$stripe.createToken(this.card) if Stripe is injected on Vue.

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();
},

Error in activated hook: "TypeError: Cannot read property '$http' of undefined"

I wrote a function to get user info and import it in other places, how can I use it to get data? Can someone please help me?
export function userInfo () {
let res
this.$http({
url: this.$http.adornUrl('/sys/user/info'),
method: 'get',
params: this.$http.adornParams()
}).then(({data}) => {
if (data && data.code === 0) {
res = data
}
})
return res
}
I use userInfo() and got error: Error in activated hook: "TypeError: Cannot read property '$http' of undefined"
import {userInfo} from './getUserInfo'
activated () {
userInfo()
}
You should define your function as a mixin in order to use component's this inside the function and you can call it using this.userInfo():
export default {
methods: {
userInfo () {
let res
this.$http({
url: this.$http.adornUrl('/sys/user/info'),
method: 'get',
params: this.$http.adornParams()
}).then(({data}) => {
if (data && data.code === 0) {
res = data
}
})
return res
}
}
}
and then use it like this:
import userInfoMixin from './getUserInfo'
export default {
// ...
mixins: [userInfoMixin],
// ...
activated () {
this.userInfo()
}
}
OR you can call your function and bind this to it like this:
import {userInfo} from './getUserInfo'
// ...
activated () {
userInfo.call(this)
}
If you have several such functions and wish to use them in several components then you should prefer the first solution.

Multiple commits in vuex action not triggering mutation

I'm attempting to set a loading status on a component which will display a loading graphic while the request is being processed. I have this working with a call to an API, returning a promise from a fetch to a REST interface. However, when attempting this with a simple call to a service it only seems to call the last mutation
In the vuex store:
var initialState = {
status: { loading: false },
all_clients: [],
current_client: {
loaded: false,
}
}
export const fund = {
namespaced: true,
state: initialState,
actions: {
getClientOverview({ commit }, client_group_id) {
commit('loadingRequest');
clientService.getClientOverview(client_group_id)
.then(
clientInfo => {
commit('loadingSuccess')
},
error => {
commit('loadingError', error);
}
)
}
},
mutations: {
// Mutations to set loading and error statuses
loadingRequest(state) {
state.status = { loading: true };
},
loadingSuccess(state) {
state.status = { loading: false };
},
loadingError(state,error) {
state.status = { loading: false };
store.dispatch('alert/error', "Error Loading Client: " + error)
},
}
and in the corresponding client service that is called:
function getClientOverview(client_group_id) {
return new Promise((resolve,reject) => {
try {
// Note here that client.state.all_clients is an array of objects already set in the store
let clientDetail = client.state.all_clients.filter(function(item) {
return item.client_group_id === client_group_id;
});
// THIS PAUSE CODE JUST ADDED TO SLOW THINGS DOWN A BIT ON FRONT END
// SO I CAN SEE IF MUTATIONS ARE WORKING CORRECTLY
var d = new Date();
var d2 = null;
do { d2 = new Date(); }
while(d2-d < 2000);
// TO HERE
if(clientDetail.length == 0){
reject("Client Information not found")
}
resolve(clientDetail[0])
}catch(error){
reject(error)
}
})
}
A client is being returned as expected, as a resolved promise, but I only see 'loading' being set to false on the front end, it doesn't set to true while the service is running its process.
If I remove the commit('loadingSuccess') line then I can see the original commit being called as expected and loading being set to true - it seems as though only one commit is being called per action
Any help with this would be gratefully appreciated!
Thanks

Computed Getter causes maximum stack size error

I'm trying to implement the following logic in Nuxt:
Ask user for an ID.
Retrieve a URL that is associated with that ID from an external API
Store the ID/URL (an appointment) in Vuex
Display to the user the rendered URL for their entered ID in an iFrame (retrieved from the Vuex store)
The issue I'm currently stuck with is that the getUrl getter method in the store is called repeatedly until the maximum call stack is exceeded and I can't work out why. It's only called from the computed function in the page, so this implies that the computed function is also being called repeatedly but, again, I can't figure out why.
In my Vuex store index.js I have:
export const state = () => ({
appointments: {}
})
export const mutations = {
SET_APPT: (state, appointment) => {
state.appointments[appointment.id] = appointment.url
}
}
export const actions = {
async setAppointment ({ commit, state }, id) {
try {
let result = await axios.get('https://externalAPI/' + id, {
method: 'GET',
protocol: 'http'
})
return commit('SET_APPT', result.data)
} catch (err) {
console.error(err)
}
}
}
export const getters = {
getUrl: (state, param) => {
return state.appointments[param]
}
}
In my page component I have:
<template>
<div>
<section class="container">
<iframe :src="url"></iframe>
</section>
</div>
</template>
<script>
export default {
computed: {
url: function (){
let url = this.$store.getters['getUrl'](this.$route.params.id)
return url;
}
}
</script>
The setAppointments action is called from a separate component in the page that asks the user for the ID via an onSubmit method:
data() {
return {
appointment: this.appointment ? { ...this.appointment } : {
id: '',
url: '',
},
error: false
}
},
methods: {
onSubmit() {
if(!this.appointment.id){
this.error = true;
}
else{
this.error = false;
this.$store.dispatch("setAppointment", this.appointment.id);
this.$router.push("/search/"+this.appointment.id);
}
}
I'm not 100% sure what was causing the multiple calls. However, as advised in the comments, I've now implemented a selectedAppointment object that I keep up-to-date
I've also created a separate mutation for updating the selectedAppointment object as the user requests different URLs so, if a URL has already been retrieved, I can use this mutation to just switch the selected one.
SET_APPT: (state, appointment) => {
state.appointments = state.appointments ? state.appointments : {}
state.selectedAppointment = appointment.url
state.appointments = { ...state.appointments, [appointment.appointmentNumber]: appointment.url }
},
SET_SELECTED_APPT: (state, appointment) => {
state.selectedAppointment = appointment.url
}
Then the getUrl getter (changed its name to just url) simply looks like:
export const getters = {
url: (state) => {
return state.selectedAppointment
}
}
Thanks for your help guys.