How to Realtime Vue from vuex - vue.js

I have to continue to develop from others. But a value is passed vuex.
mounted() {
this.fetch();
},
computed: {
...mapGetters(['getValue'])
},
methods: {
...mapActions(['fetch']),
...mapMutations({}),
async scrollBottom() {
this.fetch();
}
}
getValue is data .
fetch is a action > edit delete
if refresh page is true work properly But I would like it to be real time.
Can I have some advice?
And it would help me a lot if you have the right example.

Related

How to use async/await in vue lifecycle hooks with vuex?

When I dispatch an action in App.vue component in mounted() lifecycle hook, it runs after other components load. I am using async/await in my action and mounted lifecycle hook.
App.vue file
methods: {
...mapActions({
setUsers: "setUsers",
}),
},
async mounted() {
try {
await this.setUsers();
} catch (error) {
if (error) {
console.log(error);
}
}
},
action.js file:
async setUsers(context) {
try {
const response = await axios.get('/get-users');
console.log('setting users');
if (response.data.success) {
context.commit('setUsers', {
data: response.data.data,
});
}
} catch (error) {
if (error) {
throw error;
}
}
},
In Users list component, I need to get users from vuex. So I am using mapGetters to get Users list.
...mapGetters({
getUsers: "getUsers",
}),
mounted() {
console.log(this.getUsers);
},
But the problem is "setting users" console log in running after console logging the this.getUsers.
In Users list component, I can use getUsers in the template but when I try to console log this.getUsers it gives nothing.
How can I run app.vue file before running any other components?
You are using async await correctly in your components. It's important to understand that async await does not hold off the execution of your component, and your component will still render and go through the different lifecycle hooks such as mounted.
What async await does is hold off the execution of the current context, if you're using it inside a function, the code after the await will happen after the promise resolves, and in your case you're using it in the created lifecycle hook, which means that the code inside the mounted lifecycle hook which is a function, will get resolved after the await.
So what you want to do, is to make sure you render a component only when data is received.
Here's how to do it:
If the component is a child component of the parent, you can use v-if, then when the data comes set data to true, like this:
data() {
return {
hasData: false,
}
}
async mounted() {
const users = await fetchUsers()
this.hasData = true;
}
<SomeComponent v-if="hasData" />
If the component is not a child of the parent, you can use a watcher to let you know when the component has rendered. When using watch you can to be careful because it will happen every time a change happens.
A simple rule of thumb is to use watch with variables that don't change often, if the data you're getting is mostly read only you can use the data, if not you can add a property to Vuex such as loadingUsers.
Here's an example of how to do this:
data: {
return {
hasData: false,
}
},
computed: {
isLoading() {
return this.$store.state.app.users;
}
},
watch: {
isLoading(isLoading) {
if (!isLoading) {
this.hasData = true;
}
}
}
<SomeComponent v-if="hasData" />
if you're fetching a data from an API, then it is better to dispatch the action inside of created where the DOM is not yet rendered but you can still use "this" instead of mounted. Here is an example if you're working with Vuex modules:
created() {
this.fetchUsers();
},
methods: {
async fetchUsers() {
await this.$store.dispatch('user/setUsers');
},
},
computed: {
usersGetters() {
// getters here
},
},
Question: Do you expect to run await this.setUsers(); every time when the app is loaded (no matter which page/component is being shown)?
If so, then your App.vue is fine. And in your 'Users list component' it's also fine to use mapGetters to get the values (note it should be in computed). The problem is that you should 'wait' for the setUsers action to complete first, so that your getUsers in the component can have value.
A easy way to fix this is using Conditional Rendering and only renders component when getUsers is defined. Possibly you can add a v-if to your parent component of 'Users list component' and only loads it when v-if="getUsers" is true. Then your mounted logic would also work fine (as the data is already there).

Redo the api call everytime I change the data value on VueJs

I'm trying to update a request of an axios.get
I have a method that adds 1 to the param data (the default value is 1), but even thought I'm updating the param value, the page won't change the content because it's not updating the get requisition
I know there something similar in react with componentDidUpdate method
Here's my code
Api request
async created() {
const {
data: {
data: { items, pagination },
},
} = await this.$axios.get(`/faq?page=${this.param}`)
},
Method:
methods: {
next() {
this.param = this.param + 1
},
},
So is it possible to redo the create() everytime i use the method next?
created() hook is called only once during a lifecycle, you can use watcher instead in order to listen to variable changes
watch: {
param: {
immediate: true,
handler(newVal, oldVal) {
if (newVal !== oldVal) {
await this.$axios.get(`/faq?page=${newVal}`)
}
}
}
}
For more info, please take a look at: https://v2.vuejs.org/v2/guide/computed.html#Computed-vs-Watched-Property

Vue reactivity issues in component making asynchronous server requests

A small amount of context: I have a Vue view called Articles. When the component is mounted to the DOM, I fetch all posts from the database using the axios library (in conjunction with Laravel controllers and API routes). The articles view contains a data property called active, which points towards the post that is currently selected. Clicking on a different post in the sidebar updates active and subsequently the new post is shown.
Now, every post has many comments, and those comments in turn can be linked to subcomments if you will. However, the mounted lifecycle hook in Articles.vue gets invoked only once and when I try to place the server request in updated(), everything seemingly works but I'd eventually get a 429 status (too many requests). My guess is that for each comment that is retrieved, the code in updated() get's invoked again.
I guess my question is as follows: How can I make Post.vue reactive, since right now the mounted lifecycle hook will be invoked only once even when another post is selected.
Here's the code:
Articles.vue
export default {
name: "Articles",
components: {SidebarLink, PageContent, Sidebar, Post, Searchbar, Spinner},
data() {
return {
posts: [],
active: undefined,
loading: true
}
},
mounted() {
this.fetchPosts();
},
methods: {
async fetchPosts() {
const response = await this.$http.get('/api/posts');
this.posts = response.data;
this.active = this.posts[0];
setTimeout(() => {
this.loading = false;
}, 400);
},
showPost(post) {
this.active = post;
}
}
}
Post.vue
export default {
name: "Post",
components: {Tag, WennekesComment},
props: ['post'],
data() {
return {
expanded: true,
comments: []
}
},
mounted() {
this.fetchComments();
},
methods: {
async fetchComments() {
let response = await this.$http.get('/api/posts/' + this.post.id + '/comments');
this.comments = response.data;
}
}
}
WennekesComment.vue
export default {
name: "WennekesComment",
props: ['comment'],
data() {
return {
subComments: []
}
},
mounted() {
this.fetchSubcomments();
},
methods: {
fetchSubcomments() {
let response = this.$http.get('/api/comments/' + this.comment.id).then((result) => {
// console.log(result);
});
}
}
}
Template Logic
<wennekes-comment v-for="comment in comments" :key="comment.id" :comment="comment"></wennekes-comment>
<post v-if="!loading" :post="active" :key="active.id"/>
Thanks in advance, and my apologies if this question is somewhat unclear, I'm somewhat at a loss.
Regards,
Ryan
UPDATE
I think I got it to work. In Articles.vue, I have appended a key to the post component. I think this is Vue's way of knowing which specific instance of a component to update.
I think I got it to work. In Articles.vue, I have appended a key to the post component. I think this is Vue's way of knowing which specific instance of a component to update.

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.

How to react once to a change in two or more watched properties in vuejs

There are many use cases for this but the one I'm dealing with is as follows. I have a page with a table, and two data properties, "page" and "filters". When either of these two variables are updated I need to fetch results from the server again.
However, there is no way as far as I can see to watch two variables and react only once, especially in the complicated instance updating filters should reset page to zero.
javascript
data: {
return {
page: 0,
filters: {
searchText: '',
date: ''
}
}
},
watch: {
page (nv) {
this.fetchAPI()
},
filters: {
deep: true,
handler (nv) {
this.page = 0
this.fetchAPI()
}
}
},
methods: {
fetchAPI () {
// fetch data via axios here
}
}
If i update filters, its going to reset page to 0 and call fetchAPI() twice. However this seems like the most intuitive way to have a page with a table in it? filters should reset page to zero as you may be on page 500 and then your filters cause there to only be 1 page worth of results, and a change to either page or filters must call the api again.
Interested to see how others must be tackling this exact same problem reactively?
Take into the rule - watchers are the "last hope". You must not use them until you have other ways.
In your case, you could use events. This way the problem will go by itself:
Add #click="onPageChange" event to the page button (or whatever do you use).
Add #change="onFilterChange" event to the filter component (or whatever do you use). You can also use #click="onFilterChange" with some additional code to detect changes. Still, I am pretty sure you must have something like #change on the filter component.
Then your code will look like:
data: {
return {
page: 0,
filters: {
searchText: '',
date: ''
}
}
},
methods: {
onPageChange () {
this.fetchAPI()
},
onFilterChange () {
this.page = 0
this.fetchAPI()
},
fetchAPI () {
// fetch data via axios here
}
}
In this case, the onFilterChange will change the page data but will not trigger the onPageChange method. So your problem will not exist.
Since the filters object is really complicated and has many options i have decided to keep it on a watcher that triggers setting page to zero and reloading the api. I now solve the problem as stated below. The 'pauseWatcher' data variable is a bit messy and needing to disable it in nextTick seems unideal but its a small price to pay for not having to manually hook up each and every filters input (some of them are far more complex than one input, like a date filter between two dates) and have them each emit onChange events that trigger reloading the api. It seems sad Vuejs doesnt have a lifecycle hook where you can access data properties and perhaps route paramters and make final changes to them before the watchers and computed properties turn on.
data: {
return {
pauseWatcher: true,
page: 0,
filters: {
searchText: '',
date: ''
}
}
},
watch: {
filters: {
deep: true,
handler (nv) {
if (this.pauseWatcher) {
return
}
this.page = 0
this.fetchAPI()
}
}
},
methods: {
fetchAPI () {
// fetch data via axios here
},
goToPage(page) {
this.page = page
this.fetchAPI()
},
decodeFilters () {
// decode filters from base64 URL string
}
},
created () {
this.pauseWatcher = true
this.page = Number(this.$route.query.page) || 0
this.filters = this.decodeFilters(this.$route.query.filters)
this.$nextTick(() => {
this.pauseWatcher = false
})
}