Which Lifecycle hook after axios get but before DOM render - vue.js

I'm trying to render my DOM, dependent on some data I'm returning from an axios get. I can't seem to get the timing right. The get is in the created hook, but there is a delay between the get and actually receiving the data. Basically if there is info in seller_id then I need to show the cancel button, otherwise don't. Here is my code:
this is in my created hook
axios.get('https://bc-ship.c9users.io/return_credentials').then(response => {
this.seller_id = response.data.seller_id;
this.selected_marketplace = response.data.marketplace;
this.token = response.data.auth_token;
});
and then this is the logic to show or hide the button. I've tried created, mounted, beforeUpdate, and updated all with no luck. I've also tried $nextTick but I can't get the timing correct. This is what I have currently:
beforeUpdate: function () {
// this.$nextTick(function () {
function sellerIdNotBlank() {
var valid = this.seller_id == '';
return !valid;
}
if(sellerIdNotBlank()){
this.show_cancel_button = true;
}
// })
},

First, it is pointless to get your data from backend and try to sync with Vue.js lifecycle methods. It never works.
Also, you should avoid beforeUpdate lifecycle event. It is often a code smell. beforeUpdate is to be used only when you have some DOM manipulations done manually and you need to adjust them again before Vue.js attempt to re-render.
Further, show_cancel_button is a very good candidate for a computed property. Here is how component will look:
const componentOpts = {
data() {
return {
seller_id: '',
// ... some more fields
};
},
created() {
axios.get('https://bc-ship.c9users.io/return_credentials').then(response => {
this.seller_id = response.data.seller_id;
this.selected_marketplace = response.data.marketplace;
this.token = response.data.auth_token;
});
},
computed: {
show_cancel_button() {
return this.seller_id !== '';
}
}
}

Related

Vue component check if a vuex store state property is already filled with data

here is my setup. The state "categories" in the state is fetched async from a json endpoint.
In the component I want to work with this data, but if I reload the page the categories are always empty.
methods: {
onSubmit() {
console.log(this.filter);
},
formCats(items) {
console.log(items);
// const arr = flatten(data);
// console.log(arr);
}
},
created() {
const data = this.categories;
this.formCats(data);
},
computed: {
...mapState(['categories'])
}
I also tried async created() with await this.categories. Also not working as expected! Would be great if someone could help me with this. Thanks!
This is happening because the async fetch doesn't finish until after the component is already loaded. There are multiple ways to handle this, here's one. Remove created and turn formCats method into a computed.
computed: {
...mapState(['categories']),
formCats() {
let formCats = [];
if (this.categories && this.categories.length) {
formCats = flatten(this.categories);
}
return formCats;
}
}
formCats will be an empty array at first, and then it will immediately become your formatted categories when this.categories is finished fetching.

When passing data from parent component to child component via props, the data appears to be undefined in the mounted hook of the child component

In my parent component:
<UsersList :current-room="current_room" />
In the child component:
export default {
props: {
currentRoom: Object
},
data () {
return {
users: []
}
},
mounted () {
this.$nextTick( async () => {
console.log(this.currentRoom) // this, weirdly, has the data I expect, and id is set to 1
let url = `${process.env.VUE_APP_API_URL}/chat_room/${this.currentRoom.id}/users`
console.log(url) // the result: /api/chat_room/undefined/users
let response = await this.axios.get(url)
this.users = response.data
})
},
}
When I look at the page using vue-devtools, I can see the data appears:
I've run into this issue in the past – as have many others. For whatever reason, you can't rely on props being available in the component's mounted handler. I think it has to do with the point at which mounted() is called within Vue's lifecycle.
I solved my problem by watching the prop and moving my logic from mounted to the watch handler. In your case, you could watch the currentRoom property, and make your api call in the handler:
export default {
props: {
currentRoom: Object
},
data() {
return {
users: []
}
},
watch: {
currentRoom(room) {
this.$nextTick(async() => {
let url = `${process.env.VUE_APP_API_URL}/chat_room/${room.id}/users`
let response = await this.axios.get(url)
this.users = response.data
})
}
},
}
I don't think you really need to use $nextTick() here, but I left it as you had it. You could try taking that out to simplify the code.
By the way, the reason console.log(this.currentRoom); shows you the room ID is because when you pass an object to console.log(), it binds to that object until it is read. So even though the room ID is not available when console.log() is called, it becomes available before you see the result in the console.

Vue.js component not loading/rendering data when called via URL or F5

I have a Vue.js SPA with some pages that display data from a backend. When I navigate the pages via the navbar, everything works fine, components and data are loaded.
When I'm on the page, e.g. localhost:8080/#/mypage and press F5, the data doesn't get loaded / rendered. Same goes for when I directly navigate to the page via the address bar.
The data gets loaded in this function:
async beforeMount() {
await this.initializeData();
}
I've tried to call the method in every lifecycle hook, i.e. created, beforeCreated, mounted etc...
In the mounted lifecycle hook I'm setting a boolean property to true, so that the table is only rendered when the component is loaded (done with v-if).
mounted() {
this.componentLoaded = true;
}
Not sure if this is important, but I've tried it with or without and it doesn't work.
I would really appreciate it if somebody knew whats happening here.
EDIT:
this.applications is a prop and contains multiple applications which contain instances. I want to add some variables from the backend to each application.
console.log(1) gets printed
console.log(2) does not
initializeData: function () {
let warn = 0;
console.log("1");
this.applications.forEach(async application => {
const instance = application.instances[0];
console.log("2");
let myData = null;
try {
const response = await instance.axios.get('url/myData');
myData = response.data;
} catch (err) {
}
let tmpCount = 0;
let tmpFulfilled = 0;
myData.forEach(ba => {
if(!ba.fulfilled){
warn++;
application.baAllFulfilled = false;
}else {
tmpFulfilled++;
}
tmpCount++;
})
console.log("3");
// Assign values
this.baTotalWarnings = warn;
application.baAnzahl = tmpCount;
application.baFulfilled = tmpFulfilled;
this.componentLoaded = true;
}
Try removing the async and await keywords from your beforeMount, and remove this.componentLoaded from mounted. Set it instead in the then block (or after await) in your initializeData method. I'm not sure Vue supports the async keyword in its lifecycle methods.
Something like this:
beforeMount() {
this.initializeData(); // start processing the method
}
methods: {
initializeData() {
callToBackend().then(() => {
this.componentLoaded = true // backend call ready, can now show the table
})
}
}

VueJS data doesnt change on URL change

My problem is that when I go from one user page to another user page the info in component still remains from first user. So if I go from /user/username1 to /user/username2 info remains from username1. How can I fix this ? This is my code:
UserProfile.vue
mounted() {
this.$store.dispatch('getUserProfile').then(data => {
if(data.success = true) {
this.username = data.user.username;
this.positive = data.user.positiverep;
this.negative = data.user.negativerep;
this.createdAt = data.user.createdAt;
this.lastLogin = data.user.lastLogin;
data.invites.forEach(element => {
this.invites.push(element);
});
}
});
},
And this is from actions.js file to get user:
const getUserProfile = async ({
commit
}) => {
try {
const response = await API.get('/user/' + router.currentRoute.params.username);
if (response.status === 200 && response.data.user) {
const data = {
success: true,
user: response.data.user,
invites: response.data.invites
}
return data;
} else {
return console.log('Something went wrong.');
}
} catch (error) {
console.log(error);
}
};
Should I add watch maybe instead of mounted to keep track of username change in url ?
You can use watch with the immediate property, you can then remove the code in mounted as the watch handler will be called instead.
watch: {
'$route.params.username': {
handler: function() {
this.$store.dispatch('getUserProfile').then(data => {
if(data.success = true) {
this.username = data.user.username;
this.positive = data.user.positiverep;
this.negative = data.user.negativerep;
this.createdAt = data.user.createdAt;
this.lastLogin = data.user.lastLogin;
data.invites.forEach(element => {
this.invites.push(element);
});
}
});
},
deep: true,
immediate: true,
},
}
Your page is loaded before the data is retrieved it seems, you need put a "loading" property in the data and have a v-if="!loading" for your component then it will only render once the display is updated. Personally I would avoid watch if I can it is not great for performance of for fine grained handling.
Yes you should add wach on statement that contain user info.(you may have a problem to watch on object, so you can save user info in json, but im not sure). When user changing - call action, after recived response call mutation that should change a state, then watch this state.
And you might use better syntax to receive data from store. That is really bad idea call dispatch directly from your mouted hook, use vuex documentation to make your code better.

Mounted hook running before Created data api is finished loading

I am trying to load a function when images from a data api are finished loading. However, it looks like the function is run before the ApiService is finished and thus the TiffParser.replaceIMG() function is not working properly
Here's my setup:
data: function() {
return {
images: null,
imageLink: apiService.imgSrc,
loading: true,
errored: false
};
},
created: function() {
// fetch the data when the view is created and the data is
// already being observed
apiService
.getImages(this.$route.params.id)
.catch(error => {
console.log(error);
this.errored = true;
})
.then(response => {
this.loading = false;
this.images = response.data;
});
},
//vue js provides us `mounted()`. This means `onload` in javascript
mounted: function() {
TiffParser.replaceIMG();
}
Is mounted the correct lifecycle hook for this task?
You can create a watcher for your images.
created() {
const unwatch = this.$watch('images', function(newValue = [], oldValue = []) {
// any code here will execulte once the value of `images` changes
TiffParser.replaceIMG();
unwatch(); // remove the watcher
// Note that you cannot use ES6 arrow functions here, since arrow functions
// are bound to the parent context, and the `this` keyword
// would then not be bound correctly to the Vue instance.
});
// fetch images
}
Is mounted the correct lifecycle hook for this task?
Yes, if you need to access or modify the DOM of your component immediately before or after the initial render.
However, images would be empty when it's first mounted so using a watcher instead of the mounted hook seems more appropriate for this use case.