Unable to set Vue.js data values inside axios response - javascript

I have created an axios request to my api for two routes. Using the response data I sort posts into the correct columns inside an array. This all works as it should but then when I come to assigning the value of this array to an array inside data() i get the following error;
TypeError: Cannot set property 'boardPosts' of null
at eval (SummaryBoard.vue?2681:90)
at wrap (spread.js?0df6:25)
So I figured maybe something was wrong with the array I was trying to assign. So I tried to assign boardPosts a simple string value and I still get the same error. Why can I not set the value of boardPosts inside my axios response?
my code;
import axios from 'axios';
export default {
name: 'SummaryBoard',
data() {
return {
boardPosts: '',
}
},
created() {
this.getBoardData();
},
methods:
getBoardData() {
function getBoardColumns() {
return axios.get('http://localhost:5000/api/summary-board/columns');
}
function getBoardPosts() {
return axios.get('http://localhost:5000/api/summary-board/posts');
}
axios.all([getBoardColumns(), getBoardPosts()])
.then(axios.spread(function(columnData, postData) {
let posts = postData.data;
// add posts array to each object
let columns = columnData.data.map(obj => ({...obj, posts: []}));
posts.forEach((post) => {
// If column index matches post column index value
if(columns[post.column_index]){
columns[post.column_index].posts.push(post);
}
});
console.log(columns);
this.boardPosts = 'hello';
}))
.catch(error => console.log(error));
}
}
}

That's because you're using not using an arrow function in axios.spread(...). This means that you do not preserve the lexical this from the VueJS component, as function() {...} will create a new scope for itself. If you change it to use arrow function, then the this in the callback will refer to your VueJS component instance:
axios.all([getBoardColumns(), getBoardPosts()])
.then(axios.spread((columnData, postData) => {
// Rest of the logic here
}))
.catch(error => console.log(error));

Related

Vue.js remove object data if null or empty

i am sending object data to some api, i have many states, and not everyone is required everytime, so, i want to remove some data from object if it is null or empty string, how can i do this?
export default {
methods: {
sendData() {
axios
.post("api", this.$store.state.data)
.then((response) => console.log(response))
.catch((error) => console.log(error));
console.log(this.$store.state.data);
},
},
mounted() {
this.sendData();
},
};
here, where i have store state data, i need to send only the filled values and not everything with empty values too, how can i realize this?
Try to use the reduce method applied on object keys the return the object with non-empty fields:
export default {
methods: {
sendData() {
let filledData = Object.keys(this.$store.state.data).reduce((acc,curr)=>{
if(data[curr]){
acc[curr]=data[curr]
}
return acc;
},{})
axios
.post("https://covid19.devtest.ge/api/create", filledData )
....

How to show a variable before axios petition

I am making a get request where I save the result in a variable called name_student. How can I show this variable in other methods? or how should I declare it?
This is my code:
getStudent(){
axios.get('https://backunizoom.herokuapp.com/student/2')
.then((result) => {
console.log(result.data)
this.name_student=result.data.name
console.log(name_student)
})
console.log(name_student)
},
Shared data props
As #scar-2018 suggested in comments, you just need to declare name_student as a data prop to make the prop available across all component methods and hooks:
export default {
data() {
return {
name_student: '',
}
},
methods: {
getStudent() {
axios.get(/*...*).then((result) => {
this.name_student = result.data.name
this.updateStudentName()
})
},
updateStudentName() {
this.name_student += ' (student)'
}
}
}
Accessing async data
You commented that you're seeing undefined when you log name_student. Assuming the code in your question does not have a typo with regards to the location of the console.log() calls, the axios callback is run asynchronously, and you're logging this.name_student before axios modifies it:
axios.get(/*...*/).then((result) => {
this.name_student = result.data.name
console.log(this.name_student) // => 'foo' ✅
})
console.log(this.name_student) // => undefined ❌ (not yet set)

re-populate a directive array reactively in Vue.js

I have an array defined in my data() which gets populated through a custom directive in its bind hook as below:
import Vue from 'vue'
export default {
el: '#showingFilters',
name: "Filters",
data() {
return {
country: '' // v-modelled to a <select>
states: [],
}
},
directives: {
arraysetter: {
bind: function(el, binding, vnode) {
vnode.context[binding.arg] = Object.keys(el.options).map(op => el.options[op].value);
},
},
},
methods: {
countryChangeHandler() {
this.states.splice(0)
fetch(`/scripts/statejson.php?country=${this.country}`)
.then(response => response.json())
.then(res => {
res.states.forEach( (element,i) => {
Vue.set(this.states, i, element.urlid)
});
})
},
}
The problem starts when I want to re-populate the states array in the countryChangeHandler() method (when #change happens for the country select tag).
I used splice(0) to make the array empty first and I have then used Vue.set to make the re-population reactive, but Vue still doesn't know about it!!! The array has the correct elements though! I just don't know how to make this reactive.
PS: I searched to do this without forEach but $set needs an index.
I'd appreciate any help here.
This solution should work and maintain reactivity.
You should be able to replace the entire array without using splice or set.
I have used a closure to capture this because sometimes the fetch call interferes with the this reference, even inside a lambda expression.
countryChangeHandler() {
this.states = []
const that = this
fetch(`/scripts/statejson.php?country=${this.country}`)
.then(response => response.json())
.then(res => {
that.states = res.states.map(it=>it.urlid)
})
},

How to assign array fetched from an API to data property in Vue.js?

I am trying to fetch news articles from an external source, it returns JSON object. I want to assign its articles property to a variable in my component. Somehow this error is occurring.
Uncaught (in promise) TypeError: Cannot set property 'articles' of undefined
Any suggestions on how to overcome this problem?
export default {
name: "blog",
data() {
return {
articles: [],
};
},
mounted() {
// API call
this.fetchnews();
},
methods: {
fetchnews(){
fetch(
"----------------------news link-------------------------"
)
.then(function(response) {
return response.json();
})
.then(function(json_data) {
//console.log(typeof(json_data))
this.articles = json_data.articles
});
}
}
};
As the first contributor properly noticed - the issue is this.articles inside your latest function doesn't really point to what you need.
If you are limited to ES5 then stick to the first answer.
However if you can use ES6 then simply get advantages of short syntax:
export default {
name: "blog",
data() {
return {
articles: [],
};
},
mounted() {
// API call
this.fetchnews();
},
methods: {
fetchnews(){
fetch("----------------------news link-------------------------")
.then(response => response.json())
.then(json_data => this.articles = json_data.articles);
}
}
};
in this case this will properly point to the outer scope.
Also why do you need two then()? You could collapse them into one:
.then(response => this.articles = response.json().articles);
using function keyword creates new scope. if you use arrow syntax like () => {} you can use parent scope and set articles via this.articles
fetchnews(){
fetch()
.then((response) => {
return response.json();
})
.then((json_data) => {
this.articles = json_data.articles
});
}
inthis.articles: here this refers to the function not vue instance , so you may define this outside the function like:
let This=this
and inside your function :
This.articles = json_data.articles
This here refers to vue instance
javascript function as global scope make sure use to assign function to variables

Vue JS component not reactive when retrieving data using fetch

I have a strange problem in my Vue application.
The component looks like this:
...
<template v-for="foo in foos">
<Elm v-if="foo.visible" :key="foo.label" :bar="foo" />
</template>
...
"Elm" is a value in an object, retrieved from a JSON file.
The component is reactive if I get the JSON file locally:
<script>
import datas from "datafile.json";
...
methods: {
fillFoos() {
datas.forEach(data => {
this.foos.push(data)
})
}
},
mounted: {
this.fillFoos()
}
...
</script>
But when I retrieve the file remotely using fetch, the component is no longer reactive and no longer disappears when the foo.visible value is changed :
<script>
methods: {
getDataFromApi() {
return new Promise((resolve, reject) => {
fetch(this.apiUrl)
.then(response => {
return response.json();
})
.then(jsonResponse => {
resolve(jsonResponse);
})
.catch(e => {
...
})
})
},
fillFoos() {
this.getDataFromApi()
.then(response => {
response.forEach(data => {
this.foos.push(data);
});
});
}
},
mounted: {
this.fillFoos()
}
...
</script>
In both cases the "foos" array is correctly filled, the only difference is that the v-if directive seems to be broken in the second case.
To be more precise, the display is done correctly at initialization (foo.visible is true for all the elements and they're all displayed), but in case of a later change of the foo.visible value, they don't disappear.
I can't find what's wrong...
I believe the issue you are having is that the method getDataFromApi is returning a promise, but when you consume it in fillFoos the promise is not awaited, instead you call forEach on it.
You need to use the getDataFromApi().then(x => {}) syntax to resolve the promise, or alteratively you can use async await.
You can try something like this
methods: {
async getDataFromApi() {
const response= await fetch(this.apiUrl);
return response.json();
},
async fillFoos() {
try {
await foos = this.getDataFromApi();
this.foos = foos;
} catch(error) {
//handle error.
}
}
}
Someone posted a response very close to the solution yesterday but deleted it, I don't know why.
The problem was that I stored the fetch response in a variable in the data section, before using it to fill in the "foos" table
data: function() {
return {
dataFromApi: null
}
}
By removing this variable, and thus creating it on the fly after the fetch, everything works normally.... I didn't specify that I stored the answer in this variable because I didn't think it could be related... Morality: always specify everything !

Categories