Vuex: Best Way To Handle actions - vue.js

I am developing a CRUD application to show user-list. As an admin, the user list is shown with edit & delete icons. Once the user is deleted by dispatching deleteUser action, the user-list should be re-fetched to reflect updated list of users.
actions: {
fetchUsers(context) {
axios.get("/get/user").then(function(response) {
context.commit("SET_USERS", response.data);
});
},
deleteUser(context,payload) {
axios.delete("/delete/user",).then(function(response) {
if(response.status == 200) {
//Refresh the user list.
//HOW TO call "fetchUsers" from here.?
}
})
}
}
So I need to call fetchUsers action after delete user successful.
What is the best way to achieve this? Copying the code would help but it's against DRY principle. Delete is an example but there could be several actions like edit which would need to call fetchUsers again.
Kindly provide inputs.
Regards
Robin.

You seem to be missing a critical feature of Vuex which is actions. If you need to perform an async operation regarding a mutation, the async operations should be performed from an action. Within the action, you'll be able to perform other actions, as this answer shows.

You can use the dispatch() of the context object like this
deleteUser(context,payload) {
axios.delete("/delete/user",).then(function(response) {
if(response.status == 200) {
// payload is optional and used if you want to send specific datas to your fetchUsers method
context.dispatch('fetchUsers', payload)
}
})
}
cf: https://vuex.vuejs.org/api/#commit and https://vuex.vuejs.org/api/#actions

Related

Add test description in report

Is there a way to add custom text in testcafe reports?
I would like to add a short description in the reports, so when somebody else checks it to be able to understand what the test does (especially when the test is passed, only the test name appears in the report and I cannot write too much text in the name of the test,it makes no sense). The problem is that I have different functions checking some menus and only 1 test scenario. I would like to add a text to know which functions were called.
console.log('custom text') will write the text only in the console
this.write('custom text') used inside a async function gives an error.
class goThroughAllMenus{
constructor(){
}
async f_CheckHomeMenus() {
//Description: This function is going through all submenus under Home page
// and checks that the pages are 'up and running'.
this.write(`Running test for Home menu`)
await t
//Hjem
.click(StartPage.HomeMenus.menuHome)
.expect(StartPage.StartPage.StartSubMenu.exists).ok()
}
}
There is no built-in capability to pass custom data to the reporter. You can ping the already existing issue in the TestCafe repo. However, if you just want to send common information about the whole test block, you can use meta and create a custom reporter to show this information.
//reporter.js
export default function () {
return {
async reportTaskStart (startTime, userAgents, testCount) {
},
async reportFixtureStart (name, path, meta) {
},
async reportTestStart (name, meta) {
},
async reportTestDone (name, testRunInfo, meta) {
this.write(meta );
},
async reportTaskDone (endTime, passed, warnings, result) {
}
};
}
Also, you can create a fork of any existing reporter and enhance one.

How to array destructure a Promise.all in Nuxt's asyncData

I am working with Nuxt and Vue, with MySQL database, all of which are new to me. I am transitioning out of WebMatrix, where I had a single Admin page for multiple tables, with dropdowns for selecting a particular option. On this page, I could elect to add, edit or delete the selected option, say a composer or music piece. Here is some code for just 2 of the tables (gets a runtime error of module build failed):
<script>
export default {
async asyncData(context) {
let [{arrangers}, {composers}] = await Promise.all([
context.$axios.get(`/api/arrangers`),
context.$axios.get(`/api/composers`),
])
const {arrangers} = await context.$axios.get('/api/arrangers')
const {composers} = await context.$axios.get('/api/composers')
return { arrangers, composers }
},
}
</script>
You do have the same variable name for both the input (left part of Promise.all) and as the result from your axios call, to avoid naming collision, you can rename the result and return this:
const { arrangers: fetchedArrangers } = await context.$axios.get('/api/arrangers')
const { composers: fetchedComposers } = await context.$axios.get('/api/composers')
return { fetchedArrangers, fetchedComposers }
EDIT, this is how I'd write it
async asyncData({ $axios }) {
const [posts, comments] = await Promise.all([
$axios.$get('https://jsonplaceholder.typicode.com/posts'),
$axios.$get('https://jsonplaceholder.typicode.com/comments'),
])
console.log('posts', posts)
console.log('comments', comments)
return { posts, comments }
},
When you destructure at the end of the result of a Promise.all, you need to destructure depending of the result that you'll get from the API. Usually, you do have data, so { arrangers } or { composers } will usually not work. Of course, it depends of your own API and if you return this type of data.
Since destructuring 2 data is not doable, it's better to simply use array destructuring. This way, it will return the object with a data array inside of it.
To directly have access to the data, you can use the $get shortcut, which comes handy in our case. Directly destructuring $axios is a nice to have too, will remove the dispensable context.
In my example, I've used JSONplaceholder to have a classic API behavior (especially the data part) but it can work like this with any API.
Here is the end result.
Also, this is what happens if you simply use this.$axios.get: you will have the famous data that you will need to access to later on (.data) at some point to only use the useful part of the API's response. That's why I do love the $get shortcut, goes to the point faster.
PS: all of this is possible because Promise.all preserve the order of the calls: https://stackoverflow.com/a/28066851/8816585
EDIT2: an example on how to make it more flexible could be
async asyncData({ $axios }) {
const urlEndpointsToFetchFrom = ['comments', 'photos', 'albums', 'todos', 'posts']
const allResponses = await Promise.all(
urlEndpointsToFetchFrom.map((url) => $axios.$get(`https://jsonplaceholder.typicode.com/${url}`)),
)
const [comments, photos, albums, todos, posts] = allResponses
return { comments, photos, albums, todos, posts }
},
Of course, preserving the order in the array destructuring is important. It's maybe doable in a dynamic way but I don't know how tbh.
Also, I cannot recommend enough to also try the fetch() hook alternative someday. I found it more flexible and it does have a nice $fetchState.pending helper, more here: https://nuxtjs.org/blog/understanding-how-fetch-works-in-nuxt-2-12/ and in the article on the bottom of the page.

How to set up store date

I have data I would like to use in multiple components and manipulate. For that reason I decided to start using store, but I don't know at what stage I'm supposed to do request to the server and set store data.
The question is probably asked before but I could not find it
Your question is not clearly but if you want to centralize your logic. Your store file looks like that:
state:{
user:{
id: "",
name: "",
...
..
.
}
}
getters:{
get_user: state => state.user,
get_userID: state => state.user.id,
...
}
mutations:{
SET_USER(state, payload){
state.user = payload
},
SET_USER_ID(state, payload){
state.user.id = payload
}
...
}
actions:{
add_user({ commit }, userData){
// you can make some http request here if you have
commit("SET_USER", userData)
}
}
Basically, above code is showing you a logic. If you want to get some data which is in state, you should had a getters. If you want to change some data which is in state, you should use mutations to make this. If you want to make some functionality like post user detail to server, fetching data from server something like this you should use actions and even you can make those changes in your actions, don't. Because actions work async, mutations not.
I hope this is answer what you looking for.

Vuex Object Shows Null in Vue Component

In the app I'm creating I have a base store for objects that might be used across the entire app, such as the logged in user, validation errors and the like.
I also have other namespaced modules for specific sections of my app.
When my parent component is loaded there is an ajax call that pulls in data and commits it to the various stores.
export const instantiate = ({ commit, dispatch }) => {
return axios.get('/setup/fetch')
.then((response) => {
dispatch('base/setLoggedInUser', response.data.user, { root: true })
commit('setFetishesList', response.data.fetishes)
commit('setColorsList', response.data.colors)
commit('setRolesList', response.data.roles)
commit('setGendersList', response.data.genders)
commit('setOrientationsList', response.data.orientations)
commit('setLookingsList', response.data.lookings)
commit('setSeekingsList', response.data.seekings)
commit('setBodiesList', response.data.bodies)
commit('setHeightsList', response.data.heights)
commit('setEthnicitiesList', response.data.ethnicities)
commit('setHairsList', response.data.hairs)
commit('setEyesList', response.data.eyes)
commit('setPiercingsList', response.data.piercings)
commit('setTattoosList', response.data.tattoos)
commit('setSmokingsList', response.data.smokings)
commit('setDrinkingsList', response.data.drinkings)
commit('setStatusesList', response.data.statuses)
commit('setEducationsList', response.data.educations)
commit('setAgesList', response.data.ages)
return Promise.resolve(response)
})
}
Then I use mapped getters to access items from my stores.
computed: {
...mapGetters({
user: 'base/getUser',
fetishList: 'setup/getFetishesList',
localeData: 'setup/getLocale',
colorsList: 'setup/getColorsList',
rolesList: 'setup/getRolesList',
genderList: 'setup/getGendersList',
orientationList: 'setup/getOrientationsList',
lookingList: 'setup/getLookingsList',
seekingList: 'setup/getSeekingsList',
validation: 'base/getValidationErrors',
}),
}
All is working as expected except for my user object.
In my Vue inspector I can see that the user object is stored properly in Vuex as expected, but when I console.log(this.user) I get null and anytime I try to access a user property I get console errors.
Can anyone explain why this might be happening, I've never seen this before and have no idea what I'm looking for?
Thanks.
My guess is that your dispatch() (Vue.js actions are ALWAYS expected to be async) is not completing properly. This is how I would rewrite it with a single caveat:
Your base/setLoggedInUser Vuex action MUST return a Promise for this to work properly.
/*
export const instantiate = ({ commit, dispatch }) => {
return axios.get('/setup/fetch')
.then((response) => {
dispatch('base/setLoggedInUser', response.data.user, { root: true })
commit('setFetishesList', response.data.fetishes)
commit('setColorsList', response.data.colors)
commit('setRolesList', response.data.roles)
commit('setGendersList', response.data.genders)
commit('setOrientationsList', response.data.orientations)
commit('setLookingsList', response.data.lookings)
commit('setSeekingsList', response.data.seekings)
commit('setBodiesList', response.data.bodies)
commit('setHeightsList', response.data.heights)
commit('setEthnicitiesList', response.data.ethnicities)
commit('setHairsList', response.data.hairs)
commit('setEyesList', response.data.eyes)
commit('setPiercingsList', response.data.piercings)
commit('setTattoosList', response.data.tattoos)
commit('setSmokingsList', response.data.smokings)
commit('setDrinkingsList', response.data.drinkings)
commit('setStatusesList', response.data.statuses)
commit('setEducationsList', response.data.educations)
commit('setAgesList', response.data.ages)
return Promise.resolve(response)
})
}
*/
export const instantiate = ({ commit, dispatch }) => {
return axios.get('/setup/fetch')
.then((response) => Promise.all([
dispatch('base/setLoggedInUser', response.data.user, { root: true }),
Promise.resolve(response)
]))
.then(([dispatchResponse, response]) => {
commit('setFetishesList', response.data.fetishes)
commit('setColorsList', response.data.colors)
commit('setRolesList', response.data.roles)
commit('setGendersList', response.data.genders)
commit('setOrientationsList', response.data.orientations)
commit('setLookingsList', response.data.lookings)
commit('setSeekingsList', response.data.seekings)
commit('setBodiesList', response.data.bodies)
commit('setHeightsList', response.data.heights)
commit('setEthnicitiesList', response.data.ethnicities)
commit('setHairsList', response.data.hairs)
commit('setEyesList', response.data.eyes)
commit('setPiercingsList', response.data.piercings)
commit('setTattoosList', response.data.tattoos)
commit('setSmokingsList', response.data.smokings)
commit('setDrinkingsList', response.data.drinkings)
commit('setStatusesList', response.data.statuses)
commit('setEducationsList', response.data.educations)
commit('setAgesList', response.data.ages)
return Promise.resolve(response)
})
}
There are two main posibilities here:
The first one is that you might not be defining properly the user getter.
The second one, console.log is being executed previous to the data being set by this action:
dispatch('base/setLoggedInUser', response.data.user, { root: true })
Vuex actions are asynchronous, so setLoggedInUser could have started before the console.log (and the code giving you errors) is executed, but the actual data might not have been received yet at that point (it would be undefined).
If this is the case, add the following condition to the part of the template or the component(s) that are using the block of code where you are getting those errors:
v-if="user"
This will make Vue to wait for the mapped getter user to have a value to mount said template segment or components, avoiding trying to access properties of undefined.

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

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.