I can't seem to pass data to my Vuex action. I always get the error message TypeError: can't access property x, data is undefined. I have done a few different approaches on passing property value but I couldn't make it work. Can you look at my code, what am I doing wrong?
Store:
import axios from 'axios'
export default {
namespaced: true,
state: {
announcements: []
},
getters: {},
actions: {
createAnnouncement({ commit }, courseId, data) {
return new Promise((resolve, reject) => {
axios
.post(`teacher/courses/announcements/create/${courseId}`, { title: data.title, message: data.message })
.then((response) => {
commit('ADD_ANNOUNCEMENT', response.data.data)
resolve(response)
})
.catch((error) => { reject(error) })
})
}
},
mutations: {
ADD_ANNOUNCEMENT(state, newAnnouncement) {
state.announcements.push(newAnnouncement)
}
}
}
Component:
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
data() {
return {
title: '',
message: '',
}
},
computed: {
...mapGetters('courses', ['getCourseData'])
},
methods: {
...mapActions('announcements', ['createAnnouncement']),
onSubmit() {
const { announcementTitle, announcementMessage } = this
const courseId = this.getCourseData.id
this.createAnnouncement(courseId, { title: announcementTitle, message: announcementMessage })
}
}
}
</script>
Actions only accept two arguments, the context and an optional payload.
Try changing your action to
createAnnouncement({ commit }, { courseId, data }) {
//...
}
and dispatch it with
const payload = {
courseId: this.getCourseData.id,
data: {
title: this.announcementTitle,
message: this.announcementMessage
}
}
this.createAnnouncement(payload)
See https://vuex.vuejs.org/api/#actions
Related
I have to following situation with vue2/vuex; Let's say I have a users module where I store all users I fetched from my api.
I use this module to populate, for example, dropdown lists containing all users.
Now I also have a users page, but this page has the option to filter, paginate users etc. This happens serverside, so the module will be updated with the new list of (filtered) users.
Should I create two separate modules for both usecases (usersOptions and usersView)? To me it would seem more logical to create two instances of the user store, but apparently that's not possible with Vuex. How would you handle a situation like this?
Here is an example of the my users module:
import UserRepository from '#/repositories/UserRepository';
export default {
namespaced: true,
state: {
loading: false,
users: [],
},
getters: {
isLoading(state) {
return state.loading;
},
data(state) {
return state.users;
},
options: (state) => (value = 'id', label = 'name') => state.users.map(
(user) => ({ value: user[value], label: user[label] }),
),
},
mutations: {
SET_LOADING(state, payload) {
state.loading = payload;
},
SET_DATA(state, payload) {
state.users = payload;
},
},
actions: {
fetch({ commit }) {
return new Promise((resolve, reject) => {
commit('SET_LOADING', true);
UserRepository.index({ limit: 0 })
.then((response) => {
const users = response.data.data;
commit('SET_DATA', users);
resolve(response);
})
.catch((error) => {
reject(error);
})
.finally(() => {
commit('SET_LOADING', false);
});
});
},
},
};
Intuitively, I'd do something like that. Haven't tested it but it's probably not ready to use yet.
import UserRepository from '#/repositories/UserRepository';
export default {
namespaced: true,
state: {
loading: false,
users: [],
},
getters: {
isLoading(state) {
return state.loading;
},
data(state) {
return state.users;
},
usersView() {
return state.users.view;
},
usersOptions() {
return state.users.options;
},
options: (state) => (value = 'id', label = 'name') => state.users.map(
(user) => ({ value: user[value], label: user[label] }),
),
},
mutations: {
SET_LOADING(state, payload) {
state.loading = payload;
},
SET_DATA(state, key, payload) {
state.users[key] = payload;
},
},
actions: {
fetch({ commit }, params) {
return new Promise((resolve, reject) => {
commit('SET_LOADING', true);
UserRepository.index(params)
.then((response) => {
resolve(response.data.data);
})
.catch((error) => {
reject(error);
})
.finally(() => {
commit('SET_LOADING', false);
});
});
},
fetchOptions() {
this.dispatch('fetch', { limit: 0 }).then((users) {
commit('SET_DATA', 'options', users);
})
},
fetchView() {
this.dispatch('fetch', { limit: 15, page: 1 }).then((users) {
commit('SET_DATA', 'view', users);
})
},
},
};
Two stores its never the soultion in my opinion,
try to seperate to 2 modules.
find more here: https://vuex.vuejs.org/guide/modules.html
I have this get Request
router.get('/', async (req, res) => {
//I connect to the DB and i return the collection with Posts
const posts = await loadPostsCollection();
res.send(await posts.find({ "tmima": { "$eq": 'andriko' } }).toArray());
});
Everything works Fine as it is.. the problem is that i want to find the posts dynamicaly and not having this 'andriko' there..
I tried this { "tmima": { "$eq": req.body.params } } but its now working... the weird thing is that with req.body.params it shows all the other posts except those that have tmima: 'andriko'
This 'tmima' value comes from the dynamic route so thats why i need this req.body.params or something similar, to show the posts based on the routing.
Thx in advance!
EDIT!! FronteEnd section included!!
router.js
const routes = [
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/katigories/:tmima",
name: "katigories",
props: true,
component: () => import("../views/katigories.vue"),
children: [
{ path: 'anakoinoseis', name: 'anakoinoseis', props: true, component: () => import("../views/Ypokatigories/Anakoinoseis.vue")},
{ path: 'roster', name: 'roster', props: true, component: () => import("../views/Ypokatigories/RosterView.vue")},
{ path: 'vathmologia', name: 'vathmologia', props: true, component: () => import("../views/Ypokatigories/Vathmologia.vue")}
]
}
axios.js
import axios from 'axios'
const url = 'http://localhost:5000/api/posts/';
class AnnouncementsService {
//GET Annaouncements
static getPosts() {
return new Promise ((resolve,reject) => {
axios.get(url).then((res) => {
const data = res.data;
resolve(
data.map(post => ({
...post,
createdAt: new Date(post.createdAt)
}))
);
})
.catch((err)=> {
reject(err);
})
});
}
Anakoinoseis.vue
<script>
import AnnouncementsService from '../../store/axios'
export default {
name: 'Announcements',
props: ['tmima'],
data() {
return {
posts: [],
text: '',
title: ''
}
},
async created() {
try {
this.posts = await AnnouncementsService.getPosts();
await console.log(this.tmima)
}catch(err){
this.error = err.message;
}
},
</script>
I'm having a hard time doing this and I'm sure it's simple but I can't get it to work. I have a toggle switch with boolean value that I am successfully making it work from the Vue file but obviously vuex is yelling cause any prop change needs to be mutated in the vuex file. Here is the relevant code:
Vue file
<template>
<workstation
v-for="(workstation, index) in hStation.workstations" :key="index"
:id="workstation.recordId"
:close="workstation.closed"
#toggledState="toggleState(workstation)"
></workstation>
</template>
<script>
methods: {
...mapActions("pod", ["updateWorkstation"]),
toggleState(workstation) {
workstation.closed = !workstation.closed;
this.updateWorkstation({
recordId: workstation.recordId,
closed: workstation.closed
})
.then(response => {
console.log("id: ", workstation.recordId);
console.log("closed: ", workstation.closed);
})
.catch(error => {
console.log("error: ", error);
});
},
},
</script>
The vuex file simplified
import { axiosInstance } from "boot/axios";
export default {
namespaced: true,
state: {
workstation: []
},
getters: {
singleWorkstation: state => {
return state.workstation;
}
},
actions: {
updateWorkstation: ({ commit }, payload) => {
return new Promise((resolve, reject) => {
axiosInstance
.post("Workstation/update", payload)
.then(({ data, status }) => {
if (status === 200) {
resolve(true);
commit("setWorkstation", data.data);
}
})
.catch(({ error }) => {
reject(error);
});
});
}
},
mutations: {
setWorkstation: (state, workstation) => (state.workstation = workstation)
}
};
Error: [vuex] do not mutate vuex store state outside mutation handlers.
API schema
{
"success": true,
"data": [
{
"recordId": 0,
"worksite": 0,
"hStations": [
{
"recordId": 0,
"podId": 0,
"stationOrder": 0,
"workstations": [
{
"recordId": 0,
"name": "string",
"closed": true,
}
]
}
]
}
]
}
How do I fire the change on the close property within the mutation? Thanks in advance
Instead of passing a new object to the action, pass the whole workstation object.
this.updateWorkstation(workstation);
You'll create the posting object, postdata, inside the action, and you'll commit a second mutation for toggling when the promise resolves:
updateWorkstation: ({ commit }, workstation) => {
const postdata = {
recordId: workstation.recordId,
closed: workstation.closed
}
return new Promise((resolve, reject) => {
axiosInstance
.post("Workstation/update", postdata)
.then(({ data, status }) => {
if (status === 200) {
resolve(true);
commit("setWorkstation", data.data);
commit("toggleOldWorkstation", workstation);
}
})
.catch(({ error }) => {
reject(error);
});
});
}
Since the workstation is in the action this way, you're able to call that second mutation to toggle the closed property:
mutations: {
...
toggleOldWorkstation(workstation){
workstation.closed = !workstation.closed;
}
}
I have a CRUD component and the add user (userRegister) is giving me problems. I am able to successfully add a user, however the view doesn't refresh and the new user doesn't show unless I refresh the page.
Here's the method fired on the Submit button in my Vue Component
onSubmit() {
if (this.password === this.confirmpassword) {
this.$store
.dispatch("users/userRegister", {
operatorNumber: this.operatorNumber,
operatorName: this.operatorName,
password: this.password,
roles: this.operatorRole
})
.then(({ status }) => {
UIkit.offcanvas("#newEmployee").hide();
})
.catch(error => {
console.log("Error: ", error);
});
}
this.resetForm();
},
and my store
import { axiosInstance } from "boot/axios";
export default {
namespaced: true,
state: {
operators: []
},
getters: {
operators: state => {
return state.operators;
}
},
actions: {
async userRegister({ commit }, payload) {
return new Promise((resolve, reject) => {
axiosInstance
.post("user/create", payload)
.then(({ data, status }) => {
if (status === 200) {
resolve(true);
commit("addUser", payload);
}
})
.catch(error => {
reject(error);
console.log("Error: ", error);
});
});
},
},
mutations: {
addUser: (state, operators) => state.operators.splice(state.operators.length, 0, operators)
}
};
What am I missing? Thanks
I have the following actions in my Vuex store:
import { HTTP } from '#/services/http'
import router from '#/router'
export const actions = {
loginUser ({ commit, state }, params) {
HTTP.post('v1/login.json', { email: params.email, password: params.password })
.then(response => {
localStorage.setItem('access_token', response.data.token)
router.push({name: 'Hello'})
}).catch(error => {
commit('SET_LOGIN_ERROR', error.response.data.error)
})
},
myAccount ({ commit }) {
HTTP.get('v1/my_account.json').headers({'Authorization': ('Token token=' + localStorage.getItem('access_token'))})
.then(response => {
commit('SET_USER', response.data)
})
}
}
I want to launch myAccount action when loginUser succeeds. How can I do that?
I've tried something like this:
import { HTTP } from '#/services/http'
import router from '#/router'
export const actions = {
loginUser ({ commit, state }, params) {
HTTP.post('v1/login.json', { email: params.email, password: params.password })
.then(response => {
localStorage.setItem('access_token', response.data.token)
router.push({name: 'Hello'})
}).catch(error => {
commit('SET_LOGIN_ERROR', error.response.data.error)
})
},
myAccount ({ dispatch, commit, state }, payload) {
dispatch('loginUser', payload)
.then((res) => {
console.log('dupa')
// Do this when loginUser finished
})
}
}
but this not works...
actions receive the context object, so you can simply either pass the entire object or add dispatch to your destructuring assignment :
const store = new Vuex.Store({
actions: {
foo(context) {
console.log('foo called');
},
bar({dispatch}) {
setTimeout(() => dispatch('foo'), 1000)
}
}
});
Here's the JSFiddle: https://jsfiddle.net/y1527vxh/
Since vue actions can be asynchronous you can add dispatch handler to an action to call another action when it is done;
export const actions = {
loginUser ({ commit, state }, params) {
... // some http request or what would you like to do
},
myAccount ({ dispatch, commit, state }, payload) {
dispatch('loginUser', payload)
.then((res) => {
...
// Do this when loginUser finished
})
},
}
I am doing autentication in my projects like this, i am using axios btw:
loginUser ({ dispatch, commit, state }, payload) {
let loginData = {
username: payload.username,
password: payload.password
}
return axios.post(state.url, loginData)
.then((res) => {
// You can console.log(res.data) to see if your token data is fine
window.localStorage.setItem('AuthTokens', JSON.stringify(res.data))
dispatch('myAccount', { tokens: res.data })
})
.catch((err) => {
// Error handling...
})
},
myAccount ({ commit, state }, { tokens }) {
let headerOptions = {
// Header options with tokens.access_token...
}
return axios.get(state.url, headerOptions)
.then((res) => {
// You have the user data
console.log(res.data)
})
.catch((err) => {
// Error handling...
})
}