Reputation: 1282
Problem
Im trying to load data from my database using vuejs + axios in a Laravel project. Locally I can make the request using axios and it returns the data, assigns it to variables and then outputs the data to the DOM.
When I push the same code to production the axios request takes a little longer to execute but it seems to be long enough that vuejs is ready before all the data is returned, leaving me with an annoying 'undefined property' error when the data attempts to be displayed. Sometimes this error persist's through 20+ refreshes before the data loads.
What I think the issue is
So my guess from reading up is that axios isn't getting the data as quickly as it should do so vuejs is ready to start serving the data but axios hasn't had a chance to collect it yet, resulting in the undefined error
What i've read and tried
I've read that using v-if
statements on the objects that depend on the data should fix this issue. But it doesn't, all it does it hide the object from view if the data is not present. For Example...
HTML
<!-- If user.name exists, display user.name -->
<div v-if="user.name">
@{{ user.name }}
</div>
JAVASCRIPT
<script>
new Vue({
el: '#container',
data: {
user: {}
},
mounted() {
this.getUserData();
},
methods: {
getUserData(){
axios.post('/user/get').then((response) => {
// Check the response was a success
if(response.data.status === 'success')
{
this.user = response.data.user;
}
});
},
}
})
</script>
This doesn't work, it just doesn't display anything when the data hasn't been loaded yet.
Question
How do I ensure that my loaded data is displayed and not return an undefined error? The only way I can think is by allowing the user to 'click to retrieve data' on a failed attempt.
Further Information
Im not using vue templates, the child/parent structure or any vue libraries. I'm importing vue.js via CDN and using it on the page in a script as seen above. Im not sure if using it like this will cause any such limitations, i've only learnt vue.js on a basic and this works for the company and the boss...
Upvotes: 5
Views: 11007
Reputation: 35
<div v-if="user">
<div>
@{{ user.name }}
</div>
<div>
@{{ user.second_name }}
</div>
<div>
@{{ user.phone }}
</div>
<div>
@{{ user.any }}
</div>
</div>
Upvotes: 0
Reputation: 109
With the help of lodash's debounce you can delay the time between when a method function or computed property is called and excecuted. Also you can differentiate the delay time for the same method creating different functions eg. one being triggered by mounted and the other one by a watcher. In my case the function being called on mounted, needs to wait before the axios call returns a response and sets store.state.TableDataInit. In the watcher case it can run immediately because the store.state variable already has been set, there is no need to wait for an axios response anymore. For example:
created: function() {
this.setInitRange = _.debounce(this.setRange, 600);
this.setInitBoundaries = _.debounce(this.setBoundaries, 600);
this.setWatchRange = this.setRange;
this.setWatchBoundaries = this.setBoundaries;
},
mounted() {
this.setInitRange(this.selected);
this.setInitBoundaries();
},
watch: {
selected() {
store.commit("storedMetric", this.selected);
this.setWatchRange(this.selected);
this.setWatchBoundaries();
}
},
methods: {
setRange(value) {
var metric = value;
var dataArray = store.state.TableDataInit;
const maxMetric = dataArray.reduce(function(prev, current) {
return _.get(prev, metric) > _.get(current, metric) ? prev : current;
})[metric];
const minMetric = dataArray.reduce(function(prev, current) {
return _.get(prev, metric) < _.get(current, metric) ? prev : current;
})[metric];
this.range = [minMetric, maxMetric];
store.commit("storedOutBoundRange", this.range);
},
setBoundaries() {
this.min = store.state.OutBoundRange[0];
this.max = store.state.OutBoundRange[1];
},
Upvotes: 0
Reputation: 4067
Edited answer, to explain better what I mean.
Your code should work, no matter if it takes 1 sec or 10 sec for axios to fetch the data. You shouldn't have to check with v-if on user.name. VueJS has reactivity and upates the view if the data property changes.
See this code example. https://codepen.io/bergur/pen/VOXrzR
Here I wait 10 seconds to populate the user object
So one solution is to define address initially in the data object
user: {
address: {}
}
My point is that your initial code should work (when rendering). The undefined errors come when
a) You're using that name property in your vue app, e.g user.name when there is no name property.
or
b) When you're rendering 2nd level property, e.g user.address.country because address hasn't been initially defined.
Upvotes: 1
Reputation: 179
You could add a 'loaded' boolean attribute to resolve this. For example:
<script>
new Vue({
el: '#container',
data: {
isLoaded: false,
user: {}
},
mounted() {
this.getUserData();
},
methods: {
getUserData(){
axios.post('/user/get').then((response) => {
this.isLoaded = true;
// Check the response was a success
if(response.data.status === 'success')
{
this.user = response.data.user;
}
});
},
}
})
</script>
Then wrap your html to check if the data has been loaded:
<div v-if="isLoaded">
<!-- If user.name exists, display user.name -->
<div v-if="user.name">
@{{ user.name }}
</div>
</div>
You could then also display a loader:
<div v-if="isLoaded">
<!-- If user.name exists, display user.name -->
<div v-if="user.name">
@{{ user.name }}
</div>
</div>
<div v-else>
Loading...
</div>
Upvotes: 5