Axios can't set data - vue.js

Here's my data:
data: function(){
return {
contas: [{id: 3,
nome: "Conta de telefone",
pago: false,
valor: 55.99,
vencimento: "22/08/2016"}] //debug test value
};
},
And here's my get request:
beforeMount() {
axios.get('http://127.0.0.1/api/bills')
.then(function (response) {
console.log("before: " + this.contas);
this.contas = response.data;
console.log("after: " + this.contas);
});
},
The problem is I can't access this.contas from within axios.get(). I've tried Vue.set(this, 'contas', response.data); and window.listaPagarComponent.contas = response.data; without success.
My console shows:
before: undefined
after: [object Object],[object Object],[object Object],[object Object],[object Object],[object Object]
But Vue Devtools shows only:
contas: Array[1]
0: Object
id: 3
nome: "Conta de telefone"
pago: false
valor: 55.99
vencimento: "22/08/2016"
Here's my full code.

In option functions like data and created, vue binds this to the view-model instance for us, so we can use this.contas, but in the function inside then, this is not bound.
So you need to preserve the view-model like (created means the component's data structure is assembled, which is enough here, mounted will delay the operation more):
created() {
var self = this;
axios.get('http://127.0.0.1/api/bills')
.then(function (response) {
self.contas = response.data;
});
}
Or you can use arrow function in ES6 standard if you only aim to support modern browsers(or using a transpiler like babel), like:
created() {
axios.get('http://127.0.0.1/api/bills')
.then((response) => {
this.contas = response.data;
});
}
this inside arrow functions are bound according to lexical context, which means the this in the above snippet is the same as the one in created, which is what we want.

To be able to access this.contas inside axios.get() do you need binding "this" to keep variable usage scoped:
mounted() {
axios.get('http://127.0.0.1/api/bills')
.then(function (response) {
console.log("before: " + this.contas);
this.contas = response.data;
console.log("after: " + this.contas);
}.bind(this));
}

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.

How to run method inside a response function using Vue.js

I'm trying to run a method when I get success response from API, but the method dont run. I made a quick example here to show.
The test() function should be executed after i get the response, since its calling another API endpoint. Here is the vue.js code.
var app = new Vue({
el: "#contents",
data: {
id: null,
details: [],
},
methods: {
fetchProductDetails: function(){
let vue = this;
axios.post("/api/get-details", {
id : id
})
.then(function (response) {
vue.details = response.data;
this.test();
})
.catch(function (error) {});
},
test: function () {
console.log(app.details);
}
},
mounted: function(){
this.fetchProductDetails();
},
});
You should run vue.test() instead of this.test(), just like you use vue.details = response.data instead of this.details = response.data.
When using an unnamed function in .then(), this no longer refers to your vue application, but to the unnamed function. You could use ES6 arrow function syntax in order to avoid having to set this to a specific variable, as arrow functions use their parent's scope for this instead of setting this to refer to themselves:
axios.post("/api/get-details", { id: this.id })
.then(response => {
this.details = response.data;
this.test();
})
.catch(error => { console.log(error)});
Arrow functions (and ES6 in general) are not supported by IE11 however. so you'd need to use Babel to compile it back to something ES5 JavaScript if you need to support older browsers.

Value of 'this' for VueJs 2 in promise [duplicate]

This question already has answers here:
Scope of this in javascript function
(3 answers)
Closed 4 years ago.
Learning Vue for a simple admin panel project. I cannot access my Vue instance local variables in a method's returned promise using 'this'.
I am only able to access it by directly calling the vue instance itself. is this the correct way to handle this.
var config = {
apiKey: "",
authDomain: "",
databaseURL: "",
projectId: "",
storageBucket: "",
messagingSenderId: ""
};
firebase.initializeApp(config);
const db = firebase.firestore()
const auth = firebase.auth()
const currentUser = auth.currentUser
// firebase collections
const categoryCollection = db.collection('predefinedCategories')
var example2 = new Vue({
el: '#components-demo',
data: {
message: 'Message',
},
methods: {
initMessage: function () {
categoryCollection.get().then(
function(querySnapshot) {
message = querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data(), doc.data().name);
console.log(example2.$data.message);
console.log(this.data.message);
});
});
},
checkMessage: function () {
console.log(this.message);
}
}
})
console.log(this.data.message) should give me 'Message'. Instead it gives me undefined.
The this in the .then() callback no longer refers to your Vue instance, because it is scoped in a function. You can of course proxy it to another variable, but since you are using ES6 already, you can use arrow function. The major advantage of arrow function is that it preserves the lexical this.
Also, if you want to refer to the app's message data, it should be referenced as this.message and not this.data.message.
initMessage: function () {
categoryCollection.get().then(querySnapshot => {
message = querySnapshot.forEach(doc => {
console.log(this.message);
});
});
},

VueJS: variable is undefined inside computed only

I'm trying to make async autocomplete input with Vue, Nuxt, Axios and Buefy. It basically works, but I need to have different strings when user just starts typing and there's yet nothing to show, and when there is nothing found for such request.
I'm checking in computed variable if input value isn't empty and axios returns empty array to handle if the request address cannot be found. But it causes error
Cannot read property 'length' of undefined
The weird thing is that address variable is successfully used in other parts of my component.
My vue file below:
<template lang="pug">
b-field(label="Your address?")
b-autocomplete(
rounded,
v-model="address",
:data="data",
placeholder="Start typing",
icon="magnify",
#input="getAsyncData",
#select="option => selected = option",
:loading="isFetching"
)
template(slot="empty") {{ dummyText }}
</template>
<script>
import axios from 'axios'
import debounce from 'lodash/debounce'
export default {
data() {
return {
data: [],
address: '',
selected: null,
isFetching: false,
nothingFound: false,
test: false
}
},
computed: {
dummyText: () => {
if (this.address.length > 0 && this.nothingFound) { // This will return error
return 'There is no such address'
} else {
return 'Keep typing'
}
}
},
methods: {
getAsyncData: debounce(function () {
this.isFetching = true
axios.post('https://suggestions.dadata.ru/suggestions/api/4_1/rs/suggest/address', {
"query": this.address,
"count": 8
}, {
headers: {
'Authorization': 'Token sometoken',
'Content-Type': 'application/json',
'Accept': 'application/json',
}
})
.then(response => {
this.isFetching = false
this.data = Object.values(response.data.suggestions)
if (response.data.suggestions.length===0) this.nothingFound = true
console.log(this.address.length) // This will work
})
.catch(error => {
this.isFetching = false
console.log(error);
})
}, 300)
}
}
</script>
This is not about ssr, I've tried to init component inside mounted hook. Think I'm missing out something obvious, but I've already spent hours trying to fix this without success
Don't use arrow function ()=>{} for computed, it will cause the wrong context (not current Vue instance).
Change to function () {} then it should work fine.
And for methods, watch, you should follow same rules.
computed: {
dummyText: function () { // change to function () {}
if (this.address.length > 0 && this.nothingFound) { // This will return error
return 'There is no such address'
} else {
return 'Keep typing'
}
}
},
You can also use es2015 shorthand for a method function:
computed: {
dummyText() {
return this.address.length > 0 && this.nothingFound ? 'There is no such address' : 'Keep typing';
}
}
The Vue Documentation states not to use arrow functions on a property or callback.
You are facing this error because an arrow function wouldn't bind this to the vue instance for which you are defining the computed property as arrow functions are bound to the parent context and this.address is undefined. Same would happen if you use arrow function for methods.
Use regular function:
dummyText: function () {
console.log(this.address)
}
Or use ES5 shorthand:
dummyText() {
console.log(this.address)
}
Or If you want to keep using the arrow function, you could pass the component instance (this) as parameter because computed properties receive component instance as their first argument. :
dummyText : ctx => console.log(ctx.address)

Accessing a method property inside another method in VUE JS

How can I access a property from a method inside another method in VUEJS?
In this example, I need an "mb_id" from fetchMA method in order to use it in fetchMB args.
export default {
name: 'details',
data () {
return {
mA: {},
mB: ''
}
},
methods: {
fetchMA(id){
this.$http.get('apiurl/' + id )
.then(function(response){
this.mA = response.body;
});
},
fetchMB(id){
this.$http.get('apiurl/' + id )
.then(function(response){
this.mB = response.body;
});
}
},
created: function(){
this.fetchMA(this.$route.params.id);
this.fetchMB(this.mA.mb_id);
}
}
If I hard code a digit inside the created, such as - this.fetchMB(10); this fetches what I need, but for obvious reasons this is not feasible.
Thank you all,
-S
Returning the promise from fetchMA() will allow you to call then() on it so you can wait until this.mA has been set.
fetchMA(id){
return this.$http.get('apiurl/' + id )
.then(function(response){
this.mA = response.body;
});
},
You can then use it like this in created:
this.fetchMA(id)
.then(() => {
this.fetchMB(this.mA.mb_id);
})
It's not really clear what this.mA is from your example. You are declaring it like an array, but accessing it like an object.