in my Vue App, I am fetching some data from remote API and then using that data to create a Html table. I have decided to go with the following flow.
fetch data from API in created() hook and set data properties there & then in mounted() hook call the method that is responsible for filling up my Html table.
but what happens is if my call to the api is asyc so as soon as created() is exited mounted() is called and since i dont have my data ready yet, i get errors. how can i defer this table population until the data fetching is complete.
here is some pseudo code for the flow;
created(){
ayncJSON() {
self.data = data;
}
}
mounted(){
self.populateTable();
}
populateTable(){
//use self.data to create table
}
You should use promises....
Make an async call to fetch the data and then (it's literally 'then') do your populate function.
So it would look something like this...
mounted () {
$http.get('url_here').then( (response) => {
self.data = response.data
populateTable()
})
}
Hope that helps. If you use Vue Resource (as shown above) this includes $http.get which returns the promise for you. Check out the documentation.
Related
I have a Vue component which fetches data from a remote API passing it to a child component. When the user clicks a button, the API is called to submit the data. The API returns the updated data object and updates the component with the new data. So far, so good.
But I'm struggling for the error case. If the API call is not successful, I need to "reset" the apiData object to the initial state as received in the mounted() function. Otherwise the user would still see the values she changed but which actually failed to update.
Two apporaches come to my mind:
Refresh the data from the API for the error case
Copy the originally received data to a variable which then will be re-assigned in the error case
Or maybe there is some more "Vue-ish" way to achieve this?
<template>
<some-component v-model="apiData"></some-component>
</template>
data() {
return {
apiData: {}
}
},
mounted() {
this.apiData = ApiService.getData();
},
methods: {
async onSubmit() {
try {
const response = await ApiService.update(this.apiData);
this.apiData = response.data;
} catch (e) {
showErrorNotification();
// How to reset `apiData` to the initial state as in mounted() ???
}
}
}
The API gets an error and does not return us any new results.
If it doesn't return new results, we still have our old data (the data you want)
Aren't we waiting for this?
If so, what's the problem for us?
According to the official vue.js documentation, component data must be a function.
In my situation, i want to get data with an axios call and store this data in a component.
The problem is, if a declare this data as a function as the documentation advice to, every time i will need this component it will do a new axios call.
My main goal using vue-components was to minimize axios calls by re-using components-data through my vues, to improve performance.
Am i wrong to use vue-components in this situation?
EDIT: here's some code to clarify my question:
What the documentation recommend to do (data is a function which returns data, which means it will do the axios call everytime you want to use the data i guess.):
Vue.component('user-infos', {
data() {
userInfos = axios.get('ajax/getInfosFiche.php?action=UserInformations')
}
});
What i did (data is an object, which means if multiple vues use it and one modify the data, it will be modified for all the vues) :
Vue.component('user-infos', {
data: {
userInfos = axios.get('ajax/getInfosFiche.php?action=UserInformations')
}
});
What you need is a data store, that holds your data and can be used from different components.
You could for example take a look at VueX.
Another way is to define your own custom store, which does not need to be as heavy as VueX.
One way to do this is create and export an observable object and import it from the components that need to have access to the store.
The way I am currently using it is by making the store object available through a global mixin. That way, every component has access to "this.$store" which holds my data.
const state = Vue.observable({ data: {} });
Vue.mixin({
computed: {
$store: {
get: function() {
return state.data;
},
set: function(newData) {
state.data = newData;
}
}
}
});
You can see the full example here.
Example Implementation
I have a call in my created method which has an await.
I want to know that the results of that call are loaded so that i can conditionally show/hide things in the DOM.
Right now it looks like the DOM is being rendered before that method has completed. But I though that methods in created were called before the DOM rendered?
You're correct in assuming that the created hook runs before the component mounts. However, the lifecycle hooks are not waiting for async calls to complete. If you want to wait for that call to be completed and data to load, you can do so by using a Boolean that you set to true when your data has loaded.
Your template:
<div v-if='dataLoaded'>Now you can see me.</div>
in your vue instace
export default {
data () {
return {
dataLoaded: false
}
},
created () {
loadMyData().then(data => {
// do awesome things with data
this.dataLoaded = true
})
}
}
This way you can keep your content hidden until that call has resolved. Take care with the context when you handle the ajax response. You will want to keep this as a reference to the original vue instance, so that you can set your data correctly. Arrow functions work well for that.
Vue.component('config_input', {
template: '#config-input-template',
data() {
paterns: []
},
computed: {
testData() {
return this.paterns;
}
},
mounted() {
var self = this;
axios.get('/listPatern').then(function (response) {
self.paterns = response.data;
}
},
});
In the console show testData is an empty array. It means that inside success ajax request function the 'paterns' value didn't update yet.
Can anybody show me how to assign data response to 'paterns' value?
I have tried using axios for ajax in vue.
Unfortunately I wasted 3 hours because it did not matter what code I tried, the data was simply not being initialized.
I resorted to vue-resource and my code was working the first time!
Include the vue-resource library and change your mounted method like so:
mounted() {
var self = this;
this.$http.get('/listPatern').then(function (response) {
self.paterns = response.data;
}
}
You could read VueJS Tutorials carefully.
When you pass a plain JavaScript object to a Vue instance as its data option, Vue will walk through all of its properties and convert them to getter/setters using Object.defineProperty. This is an ES5-only and un-shimmable feature, which is why Vue doesn’t support IE8 and below.
The getter/setters are invisible to the user, but under the hood they enable Vue to perform dependency-tracking and change-notification when properties are accessed or modified. One caveat is that browser consoles format getter/setters differently when converted data objects are logged, so you may want to install vue-devtools for a more inspection-friendly interface.
The reactive data which VueJS do something like "proxy",thus you couldn't assign to value in Object.
// Wrong
self.paterns = response.data;
// Right
self.someObject = Object.assign({}, self.someObject, response.data)
And data should be a Object
data: {
patterns: []
}
I have a component whose data is initialized by ajax. I know vue.js has provide several lifecycle hooks: Lifecycle-Diagram. But for ajax to initialize the data, which hook(beforeCreate, create, mounted, etc) is the best place to do it:
hook_name: function() {
ajaxCall(function(data) {
me.data = data;
});
}
Currently, i do it in mounted, making it to re-render the component. But i think we should get the data before the first render. Can someone figure out the best way to do it?
If you want to initialize your component with data you receive from a request, created() would be the most appropriate hook to use but it is a request, it might not resolve by the end of created or even mounted() (when even your DOM is ready to show content!).
So do have your component initialized with empty data like:
data () {
return {
listOfItems: [],
someKindOfConfig: {},
orSomeSpecialValue: null
}
}
and assign the actual values when you receive them in your created hook as these empty data properties would be available at that point of time, like:
created () {
someAPICall()
.then(data => {
this.listOfItems = data.listOfItems
})
/**
* Notice the use of arrow functions, without those [this] would
* not have the context of the component.
*/
}
It seems like you aren't using (or aren't planning to use) vuex but I'd highly recommend you to use it for for managing your data in stores. If you use vuex you can have actions which can make these api calls and by using simple getters in your component you would have access to the values returned by the request.