computed property not reading data initialized in created - vue.js

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 :-)

Related

How to return promise result from Vuex action instead of the promise itself?

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.

Dynamic post entry

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.

Returning a getters in a computed create a loop

I am calling inside the computed an action from the store to run it and after I am returning a getter, this will create a loop.
The HTML
{{loadedProjects}}
The computed
computed: {
loadedProjects() {
this.$store.dispatch("getProjects");
return this.$store.getters.loadedProjects;
}
}
The store
import Vuex from "vuex";
import axios from "axios";
const createStore = () => {
return new Vuex.Store({
state: {
loadedProjects: []
},
mutations: {
setProjects(state, projects) {
state.loadedProjects = projects
}
},
actions: {
getProjects(vuexContext) {
console.log("hello1")
return axios.get("THE API URL")
.then(res => {
console.log("hello2")
vuexContext.commit("setProjects", res.data);
})
.catch(e => console.log(e));
}
},
getters: {
loadedProjects(state) {
return state.loadedProjects;
}
}
});
};
export default createStore;
I expect to call my action to populate my state and after to return my state to render my data.
What is the point of using the store action that makes an API call inside the computed property ... maybe you want to trigger loadedProjects change ? ....computed property is not asynchronous so either way the return line will be executed before the you get the response... you might try vue-async-computed plugin OR just use the call on the created hook like you have done which is the better way and you don't have to use a computed property you can just {{ $store.getters.loadedProjects }} on your template
Computed properties should not have side effects (e.g. calling a store action, changing data, and so on). Otherwise it can happen that the triggered side effect could lead to a re-rendering of the component and possible re-fetching of the computed property. Thus, an infinite loop
I changed the code like that:
created: function () {
this.$store.dispatch("getProjects")
},
computed: {
loadedProjects() {
return this.$store.getters.loadedProjects
}
}
It is working now but I would like to know but I have that problem working inside the computed and also I wonder if it's the best solution. Any help????

How to pass variable from Vuex store to a function as value?

So I am working with what would appear to be a simple issue, but it is eluding me this evening. I have a value that is set in a Vuex store. In my component file, I declare a constant where the value is retrieved from the store. Everything up to this point works perfectly.
Then, upon submitting a form in the component a script function is run. Within that function, I need to pass the value from the Vuex store along with a couple of other arguments to another function. The function gets call, the arguments are passed, and it all works as expected.
However ... I am getting console errors stating ...
Error in callback for watcher "function () { return this._data.$$state }": "Error: [vuex] do not mutate vuex store state outside mutation handlers.
What is the correct what to retrieve a value from the Vuex store and then pass that value to a function?
Some more detail here ... Page 1 stores an object representing a CognitoUser in the store using a mutation function which works as expected, then transitions to Page 2. Page 2 retrieves the object from the store (tried both the data and computed methods mentioned below as well as using the getter directly in the code - all fail the same). Within a method on Page 2, the object from the store is accessible. However, that method attempts to call the Amplify completeNewPassword method, passing the CongnitoUser object as an argument. This is the point that the error appears stating that the mutation handler should be used even though there is no change to the object on my end.
....
computed: {
user: {
get(){
return this.$store.getters[ 'security/localUser' ]
},
set( value ){
this.$store.commit( 'security/setLocalUser', value )
}
}
},
....
methods: {
async submitForm(){
this.$Amplify.Auth.completeNewPassword( this.user, this.model.password, this.requiredAttributes )
.then( data => {
....
This is almost certainly a duplicate question. You can refer to my answer here.
Basically you should pass the Vuex value to a local data item and use that in your component function. Something like this.
<script>
export default {
data: () => ({
localDataItem: this.$store.getters.vuexItem,
})
methods: {
doSomething() {
use this.localDataItem.here
}
}
}
</script>
The canonical way of handling this by using computed properties. You define a computed property with getter and setter and proxy access to vuex thru it.
computed: {
localProperty: {
get: function () {
return this.$store.getters.data
},
set: function (val) {
this.$store.commit(“mutationName”, val )
}
}
}
Now you can use localProperty just as we use any other property defined on data. And all the changes get propagated thru the store.
Try if this work
<template>
<div>
<input :value="user" #change="onChangeUser($event.target.value)"></input>
</div>
</template>
<script>
computed: {
user() {
return this.$store.getters[ 'security/localUser' ]
}
},
methods: {
onChangeUser(user) {
this.$store.commit( 'security/setLocalUser', user );
},
async submitForm(){
this.$Amplify.Auth.completeNewPassword( this.user, this.model.password, this.requiredAttributes )
.then( data => {
...
}
</script>

Vue.js, VueX - cannot render data from API in a component

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 :)