How to refresh v-data-table after REST API Patch call? - vue.js

I have a data table in Vuetify that is populated via a REST get request, using a function "getData" that is called when the app is mounted. The <td>'s in the table have buttons that the user can hit to "lock" the period (the row/column intersection).
When they hit the button, they get a popup confirmation dialog. When they hit "OK", there is a save method called to write the current date back to the db via a REST PATCH request (see below).
My problem is, the grid is not updating with the results of the patch request. I have to manually refresh the page to see the result. What is the common pattern here? Should i pull down the data again via getData to refresh the table? Should i update the array that the data-table sits on directly?
getData method:
getData() {
var self = this;
return axios
.get("http://127.0.0.1:5000/api/estimatefinal/periods?dataset=capital")
.then(function(response) {
self.periods = response.data;
})
.catch(function(error) {
alert(error);
});
},
Save method:
save(item) {
var self = this;
axios
.patch("http://localhost:5000/api/estimatefinal/period/" + self.id, {
date: moment(self.selected_date, "YYYY-MM-DD").format(
"YYYY-MM-DDTH:m:s"
)
})
.then(function() {
this.getData(); // ????
})
.catch(function(error) {
alert(error)
});
this.getData(); // ????
this.close();
}

If your PATCH changes only one row in DB, means has visually effect on only one row on your v-data-table, then you can change the data locally when you get "success" response from back-end.
If, in other hand, your PATCH changes many other things in DB (also in v-data-table) your best option is probably to getData() after you get PATCH response.
Point is to keep that same "picture" of values in DB and on screen v-data-table.

Related

useInfiniteScroll utility of Vueuse is fetching same items again

Here is a reproducable stackblitz -
https://stackblitz.com/edit/nuxt-starter-jlzzah?file=components/users.vue
What's wrong? -
My code fetches 15 items, and with the bottom scroll event it should fetch another 15 different items but it just fetches same items again.
I've followed this bottom video for this implementation, it's okay in the video but not okay in my stackblitz code:
https://www.youtube.com/watch?v=WRnoQdIU-uE&t=3s&ab_channel=JohnKomarnicki
The only difference with this video is that he's using axios while i use useFetch of nuxt 3.
It's not really a cache issue. useFetch is "freezing" the API URL, the changes you make to the string directly will not be reliably reflected. If you want to add parameters to your API URL, use the query option of useFetch. This option is reactive, so you can use refs and the query will update with the refs. Alternatively, you can use the provided refresh() method
const limit = ref(10)
const skip = ref(20)
const { data: users, refresh: refreshUsers } = await useFetch(
'https://dummyjson.com/users',
{
query:{
limit,
skip
}
}
);
//use the data object directly to access the result
console.log(users.value)
//if you want to update users with different params later, simply change the ref and the query will update
limit.value = 23
//use refresh to manually refresh the query
refreshUsers()
This results in a first API call http://127.0.0.1:8000/api/tasks?limit=10&skip=20 and then a second with the updated values http://127.0.0.1:8000/api/tasks?limit=23&skip=20
You can leave the cache alone, as it is just a workaround, and will not work reliably.
[Updated] The useFetch() documentation is now updated as described below.
The query option is not well documented yet, as discussed in this nuxt issue. I've created a pull request on nuxt/framework to have it reflected in the documentation. Please see a full explanation below:
Using the query option, you can add search parameters to your query. This option is extended from unjs/ohmyfetch and is using ufo to create the URL. Objects are automatically stringified.
const param1 = ref('value1')
const { data, pending, error, refresh } = await useFetch('https://api.nuxtjs.dev/mountains',{
query: { param1, param2: 'value2' }
})
This results in https://api.nuxtjs.dev/mountains?param1=value1&param2=value2
Nuxt3's useFetch uses caching by default. Use initialCache: false option to disable it:
const getUsers = async (limit, skip) => {
const { data: users } = await useFetch(
`https://dummyjson.com/users?limit=${limit}&skip=${skip}`,
{
initialCache: false,
}
);
//returning fetched value
return users.value.users;
};
But you probably should use plain $fetch instead of useFetch in this scenario to avoid caching:
const getUsers = async (limit, skip) => {
const { users } = await $fetch(
`https://dummyjson.com/users?limit=${limit}&skip=${skip}`
);
//returning fetched value
return users;
};

Bind asyncData() with client side data the second time in SSR Nuxtjs vue

Initial load of the page, data gets loaded with asyncData(). But I select another location , the data changes onchange of location and binds to dlist, But when i refresh my page it again selects my default location as Bangalore and i get my old dlist .
My question is suppose i save my location value in a cookie,how do i access inside asyncData method. Or on initial load ow do i call only asyncData and rest of the times when pageloaded for 2nd or 3rd time(suppose i go to next page and click on back) only created method. can anyone help me
async asyncData () {
let dList = []
let location="Bangalore";
//Location keep on changing, default can be Bangalore. It is got from the location user selects
await fireDb.collection("drive_car")
.where("location", "==",location ).get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
cdList.push({
key: doc.id,
carbrand: doc.data().carbrand,
carclass: doc.data().carclass
});
});
});
return {
dList: dList
}
},

Upload image need to refresh using $emit / $on in Vue

I have a method that calls the user data via axios
// method name getUser()
const user = await axios.get(`/user/${this.id}`)
this.id = user.data.data.id
this.name = user.data.data.name
this.email = user.data.data.email
I then use that in the mounted so if user visits /profile/id
it'll load the user data
mounted() {
this.getUser()
}
I tried to upload an image and I emit the event using global event bus once the image is successfully uploaded.
this.$event.$emit('IMAGE_UPLOAD')
Then catch that on the mounted too
mounted () {
// if I remove this it works, but I need to preload the data of the user
this.getUser()
this.$event.$on('IMAGE_UPLOAD', () => {
this.getUser()
})
}
my problem is it doesn't change the image meaning I still need to refresh the page if I call the this.getUser() too inside the mounted.
So I'm wondering how to work around this.
Thanks!
Since the url and name of the image does not change when the new image is uploaded the image in the browser is not updated. So what I have done in the past is a little trick to essentially change the url to the image by adding a unique query parameter. So use a data property for the location of your user image and in your method where you update the users data also update the img url and add something unique to the query parameter. I usually use new Date().getTime(). So you will end up with something like /img/user-xxxx.png?1559289852686
data(){
userImg: '/img/user-xxxx.png'
},
methods:{
getUser(){
//... get your user data
.then((data)=>{
this.userImg = data.user.img +'?'+ new Date().getTime();
})
}

vue.js how to show progress if processing data

I have a page build in vue.js where an API is called (using axios) getting a large json object (1k+).
While I am processing this object, I want to show user the progress of the operation - nothing fancy like progress bars, just a simple "Processed X from Y".
I have found example on how to do this in jquery of pure JS, but I can get this to work in vue.
Any help is appreciated.
Here is my skeleton code:
<div>Processed {{processed.current}} of {{processed.total}} records</div>
<script>
data() {
return {
progress:{
current:0,
total: 0
},
records: [],
};
},
mounted() {
this.getRecords();
},
methods: {
getRecords(){
axios({
method: "GET",
url: process.env.VUE_APP_REPORTING_API + "/Reports/orders",
headers: {
"content-type": "application/json",
Authorization: this.$cookie.get("wwa_token")
}
}).then(
result => {
this.progress.total = result.data.length;
//and here where the loop should happen, something like this
//obviously the below won't work :)
result.data.forEach(function(item) {
this.records.push(item);
this.progress.current++;
}
},
error => {
}
);
}
}
</script>
Well, the obvious problem with the posted code is that it has a syntax error (missing a closing parenthesis) and it establishes a new context. The code would (sort of) "work" if it was changed it to use arrow functions:
result.data.forEach(item => {
this.records.push(item);
this.progress.current++;
});
However, I don't think that's going to do what you want. The JavaScript code will process all the items before the user interface updates, so all the user would see would be "Processed N of N records". Even if you inserted a this.$forceUpdate() in the loop to make the interface update at each iteration, the changes would still be too fast for any user to see.
The real problem is that "processing" all the items only takes a few milliseconds. So it's always going to happen too quickly to show intermediate results.
If you're trying to show the progress of the AJAX request/response, that's an entirely different question which will require coordination between the client and the server. Search for HTTP chunked responses for a start.

Resetting to initial data in Vue

I've got some form data that I display using a readonly input that is styled to look like plain text. When users click an edit button, they can then edit the inputs and either save or cancel.
My issue is obviously that when a user clicks cancel, the data they entered into the input remains (even though it isn't saved to the DB). I'm trying to figure out a way to reset the input to its initial data. I'm aware of this answer, but it doesn't seem to work because the data is fetched on creation.
This fiddle is similar except for the fact that the data in the real app comes from an axios call. The equivalent call is essentially:
fetch() {
axios.get(this.endpoint)
.then(({data}) => {
this.name = data.data;
});
}
Annoyingly, the fiddle actually works. However in my actual implementation it doesn't. The only difference with the app is that the data is an array.
How can I make this work?
This fiddle represents what my code actually does.
In the code:
data: () => ({
endpoint: 'https://reqres.in/api/users',
users: [],
initialData: []
}),
//...
edit: function(index) {
this.users[index].disabled = false
this.initialData = this.users
},
reset: function(index) {
this.users[index].disabled = true
this.users = this.initialData
}
Since users and initialData are arrays, you must use index when you access them.
So, at first sight, the change would be from:
this.initialData = this.users
To
this.initialData[index] = this.users[index]
But this won't work. Since this.users[index] is an object, whenever you change it, it will change what this.initialData[index] holds, since they are both just pointing to the same object. Another problem is that when you set it like that, the initialData won't be reactive, so you must use Vue.set().
Another thing, since you just want to reset the first_name property (the one you use at <input v-model="user.first_name" >), you should then assign user[].first_name to initialData[index].
Considering those changes to edit(), in the reset() method, the addition of [index] and of the .first_name field are enough. Final code:
edit: function(index) {
this.users[index].disabled = false
Vue.set(this.initialData, index, this.users[index].first_name);
},
reset: function(index) {
this.users[index].disabled = true
this.users[index].first_name = this.initialData[index]
}
JSFiddle: https://jsfiddle.net/acdcjunior/z60etaqf/28/
Note: If you want to back up the whole user (not just first_name) you will have to clone it. An change the order of the disabled property:
edit: function(index) {
Vue.set(this.initialData, index, {...this.users[index]});
this.users[index].disabled = false
},
reset: function(index) {
Vue.set(this.users, index, this.initialData[index]);
}
JSFiddle here. In the example above the clone is created using the spread syntax.
Input is immediately updating the model. If you want to do something like edit and save you have to take a copy and edit that. I use lodash clone to copy objects then update the fields back when save is clicked. (of course sending message to server.)