I have an axios call to an api that calls 22 objects in an array.
I want to save this from a computed, to data, then i want to loop through this in a select option with v-for.
Something like this:
<option v-for="item in items">{{item}}</option>
computed: {
items(){
return axios.post('API Call').then(response =>{ this.items =
response.data });
}
}
But I cannot for the life of me figure out where im going wrong.
As is usually the case, i resolved this by just hacking away at it. this is what i did to resolve the issue:
option:
<option v-for="item in data" :value='item.id'>{{item.name}</option>
method:
test_data: function(){
var self = this;
axios.post('API Call Here', {})
.then(response => {
self.data = response.data;
})
.catch(function(error){
console.log(error);
})
}
And the data:
data () {
return {
data:''
}
}
Assigning this.items (as if it were a variable) inside this.items (which is a function: the computed property items) looks kinda wrong to me, but I've never seen that so I don't know the effect that has.
On the other hand, axios returns a promise, so if your computed is doing return axios.whatever().then(etc) you are going to get a promise out of the computed property, which is probably not what you want.
I think in this case you want to have items be a normal data property, and then do the axios call in a method or life cycle hook, or a watcher, and then when you do this:
data() {
return {
items: [some default initial value here]
}
},
created() {
var self = this;
axios.post('API Call').then(response => {
self.items = response.data
});
}
items (now a data prop) will get assigned to response.data whenever the promise resolves successfully. (Notes: You probably know how your api formats your responses, but just in case, make sure response.data is actually what you want instead of, say, response.body.data or something. And make sure to add a catch or a rejection handler somewhere at some point too. EDIT: Whoops yeah, the other answer reminded me I forgot the "self")
Related
I have an action in my vuex store:
export const actions = {
myaction() {
return 'foo'
}
}
Can I get the promise result (here foo) in the mounted() life cycle hook and display it in the console ? If yes, how ?
I tried this:
mounted() {
console.log(
this.$store
.dispatch('myaction')
.then(res => res)
)
}
But it returns the promise instead of the promise result I'm expecting.
Either of these should work:
Using .then():
mounted() {
this.$store
.dispatch('myaction')
.then(res => console.log(res));
}
Or if you're using ES2017 or later (or some compatibility tool like Babel), then you can use async/ await:
async mounted() {
const res = await this.$store.dispatch('myaction');
console.log(res);
}
As #Dan mentions below, whilst this will return you the value of the Promise, this is not the intended usage of Vuex, which prefers all data to be saved to and accessed from Vuex's state.
It would be better to mutate the store in your action to save the value and then use a computed property in your component to retrieve it from state, either directly or through a getter. This can be done very cleanly with the mapGetters Vuex helper function.
I'm trying to create pagination for my site on Vue.js.
I have two buttons:
<button #click.prevent="onPrevBtnClick">previous</button>
<button #click.prevent="onNextBtnClick">next</button>
Which pass to my main component:
methods: {
onPrevBtnClick () {
this.$emit('prev-button')
},
onNextBtnClick () {
this.$emit('next-button')
}
}
Basically, I want to make a query according to currentPage
async fetchMovies () {
try {
const fetchData = await axios.get(`${process.env.VUE_APP_API_URL}/discover/movie&api_key=${process.env.VUE_APP_API_KEY}&page=${this.currentPage}`)
this.movies = {...fetchData.data.results}
}
catch (e) {
console.log(e)
}
},
async onPrevBtnClick () {
this.currentPage--
},
async onNextBtnClick () {
this.currentPage++
}
I try to change them through a method, also tried through the computed but for some reason will not change dynamically.
I will be grateful for your help.
If you want this.movies to be updated when the user clicks "previous" or "next" you'll have to call the fetchMovies method in the onPrevBtnClick and onNextBtnClick methods. Currently onPrevBtnClick and onNextBtnClick only update this.currentPage, but doing so will not automatically cause fetchMovies to execute if fetchMovies is a method.
An alternative approach would be to make this.movies a computed property that is based on this.currentPage and get rid of fetchMovies, putting that logic in the computed property.
Check out the guide on Computed Properties and Watchers for more information. The key things to understand are:
"computed properties are cached based on their reactive dependencies. A computed property will only re-evaluate when some of its reactive dependencies have changed."
Methods never reevaluate on their own like computed properties. Methods need you to explicitly call them.
I've been searching around for possible answers to my question but I couldn't find anymore suggestions.
The structure of my project is as follows. I have a PoolMainPage where I show some information of the activePool Object. Within the PoolMainPage there are options to direct to subpages (TeamSelector and PoolStandings), which also require information of the activePoolObject.
The activePool Object is fetched from an endpoint in the Vuex store. The code for that is the following:
const actions = {
getActivePool({ commit }) {
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
withCredentials: true
}
const activePoolId = localStorage.getItem('activePoolId')
if (activePoolId) {
return axios.get('/pools/pool-information?poolId=' + activePoolId, config)
.then((response) => {
commit('SET_ACTIVE_POOL', response.data)
return response
})
.catch((error) => {
return Promise.reject(error)
})
} else {
commit('SET_ACTIVE_POOL', null);
return;
}
}
}
The endpoint does it's job and returns the activePool object. In my PoolMainPage component (as well as in the subcomponents) I want to call this action and fetch the object, using:
created() {
if (!this.$store.getters.activePool) {
this.$store.dispatch("getActivePool");
}
},
I added the check to prevent that the endpoint is called everytime the page is refreshed and the activePool is already set. To actually load the activePool into the component, I created a computed property:
computed: {
activePool() {
return this.$store.getters.activePool;
},
},
This all works when the endpoint has returned its data, but before that I receive an error on another computed property, which is dependent on the activePool object:
maxToSpend() {
return this.activePool.inGameBudget;
},
Question 1: how do I make sure that maxToSpend does not compute until the activePool actually set? I can simply add an additional check if (this.activePool) {, but then I would have to do that for all of the computed properties.
Question 2: I don't know if this is possible, but how do make sure that I don't have to add the code to fetch the activePool from the endpoint and get it using the computed property within each of the components that I created: TeamSelector and PoolStandings?
All help/suggestions are appreciated! Let me know if anything is unclear or requires some additional information.
Thanks!
Jeroen
How do I make sure that maxToSpend does not compute until the activePool actually set?
Basically you cannot do that. The computed properties are compute right after component create. See Lifecycle Diagram, computed properties are compute at Init injections & reactivity state.
I can simply add an additional check if (this.activePool) {, but then I would have to do that for all of the computed properties.
You case use Getters:
state: {
...
},
getters: {
maxToSpend: state => {
if (!state.activePool) return
return state.activePool.inGameBudget
}
},
actions: {
...
}
Then you can use as:
computed: {
maxToSpend () {
return this.$store.getters.maxToSpend
}
}
I don't know if this is possible, but how do make sure that I don't have to add the code to fetch the activePool from the endpoint and get it using the computed property within each of the components that I created: TeamSelector and PoolStandings?
Basically no. But if both TeamSelector and PoolStandings have common a parent (might be PoolMainPage?) then you can call it only once from that parent.
In my opinion the way to explicitly dispatch mandatory action for every page its needed it's not a bad idea.
You can mount activePool getter to avoid errors when accessing nested properties. Like:
// in getters.js
const activePool = (state) => ({
...state.activePool,
maxToSpend: state.activePool && state.activePool.inGameBudget,
})
activePool.inGameBudget will always be there, regardless the Promise. So now you can access this.activePool.inGameBudget; in .vue file and you wont get the error.
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 :)
I am not sure when computed property (in vue lifecycle) comes. Let's say I have a method that I run in created() as:
created () {
getSomething()
}
Inside getSomething() I fetch data and fill my data property as:
getSomething(){
axios.get(...) { this.info = response.data }
}
Now, in computed properties, I do:
computed: {
doSomething () {
this.info.forEach(item => {})
}
}
But, inside my computed I get forEach is undefined, as if this.info is not an array or has not be filled.
What am I doing wrong? are computed props called before created() or something?
try something like this
getSomething(){
return axios.get(...) { this.info = response.data }
}
then you can use the promise returned in created method like this....
created () {
getSomething().then( () => {
doSomething()
}}
}
You could utilise Vuex' state management...
Vuex has :
Dispatches
Actions
Mutations
Getters
What i am thinking is, on your page load, map an action to the store that makes a call to your axios service and after it returns, commit a mutation that stores the response to the store...
then you can map the state to your component and retrieve the data.
This might not be the quickest answer but if you are expecting to need the response data from the axios request, it might be worth putting it into some sort of state management :-)