I'm creating a system comment with Laravel 5.5, Vue 2 and Vuex.
I Can't post a comment. I get in my console, two errors:
TypeError: this.addComment is not a function
Error: Request failed with status code 422
This is my code:
import { addComment } from '../../store/actions'
export default {
computed: {
addComment
},
vuex: {
actions: { addComment }
},
data () {...},
methods: {
sendComment: function () {
this.addComment({
commentable_id: this.id,
commentable_type: this.model,
content: this.content,
reply: this.reply
})
}
actions.js code
export const addComment = function ({dispatch}, comment) {
return axios.post('/comments', comment).then((response) => {
dispatch('ADD_COMMENT', response.data)
})
};
All my routes, controller and mutations are tested and work well.
You don't need to import actions into your components as long as the store is registered globally. So you simply need to call addComment like this:
this.$store.dispatch('addComment', {
commentable_id: this.id,
commentable_type: this.model,
content: this.content,
reply: this.reply
})
Also, putting addComment in computed doesn't make sense so you have to remove it.
In your addComment action, I believe it's called commit not dispatch:
export const addComment = function ({commit}, comment) {
return axios.post('/comments', comment).then((response) => {
commit('ADD_COMMENT', response.data)
})
}
My Store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export const state = {
comments: []
};
export const mutations = {
ADD_COMMENTS (state, comments) {
state.comments.push(...comments)
},
ADD_COMMENT (state, comment) {
if (comment.reply) {
let c = state.comments.find((c) => c.id === comment.reply);
if (c.replies === undefined) {
c.replies = []
}
c.replies.push(comment)
} else {
state.comments.push(comment)
},
DELETE_COMMENT () {...}
};
let store = new Vuex.Store({
state: state,
mutations: mutations
});
export default store;
My Form.vue Component:
import { addComment } from '../../store/actions'
import { mapActions } from 'vuex'
export default {
vuex: {
actions: { addComment }
},
data () {
return {
content: '',
loading: false
}
},
props: {
id: Number,
model: String,
reply: {
type: Number,
default: 0
}
},
methods: {
sendComment: function () {
this.loading = true;
this.$store.dispatch('addComment', {
commentable_id: this.id,
commentable_type: this.model,
content: this.content,
reply: this.reply
}).catch((error) => {
this.error = error.response.data
}).then(() => {
this.loading = false
})
}
}
}
Related
I am new to Vuex and trying to use mapActions to fetch some data from my store, and send it into my component's template. I keep getting the error message
[vuex] unknown action type: getItemDetail but I don't know why.
In my store, the action I'm trying to dispatch is getItemDetail. My full store is
import fetch from './fetch';
const url = 'items';
const defaults = {
id: '',
rating: '',
type: '',
};
const state = { ...defaults, };
const getters = {};
const actions = {
getItemDetail ({ commit, }, itemId) {
fetch.get(`${url}/${itemId}`).then((response) => {
commit('setItemDetail', { ...defaults, ...response.data, });
});
},
};
const mutations = {
setItemDetail (state, item) {
state.item = item;
},
};
export default {
namespaced: true,
state,
getters,
actions,
mutations,
};
In my component I have:
<template>
<div>
<p> {{ itemDetail }} </p>
</div>
</template>
<script>
import { mapActions } from 'vuex';
export default {
computed: {
itemDetail () {
this.getItemDetail('23451');
return this.$store.state;
},
},
methods: {
...mapActions([
'getItemDetail',
]),
},
};
</script>
Any help would be much appreciated!!!
From what I see of your code, you are in a namespaced store module.
To access a namespaced action, you need to map it with the name of the store module as the first parameter (the same should be applied for any mapState or mapGetter) :
methods: {
...mapActions("yourModuleName", [
'getItemDetail',
]),
},
I am trying to send my vuejs front end data using Vuex and Vue-axios to the backend. I have create a vuex store and vue-axios services But I get an error saying [vuex] unknown action type: addGeneral when I try to pass the data.
This is my vuex folder structure:
-store
-modules
-app
-mutations.js
-state.js
-general.js
-index.js
-actions.js
-getters.js
-index.js
-mutations.js
-state.js
This is module/general.js :
import { ApiService } from '#/services/api.service'
import { FETCH_GENERAL,
ADD_GENERAL
} from '../actions'
import { FETCH_START,
FETCH_END,
SET_GENERAL,
SET_ERROR,
} from '../mutations'
const state = {
general: [],
errors: {},
loading: false
};
const getters = {
general (state) {
return state.general;
},
isLoading (state) {
return state.loading;
}
};
const actions = {
[FETCH_GENERAL] (context, payload) {
context.commit(FETCH_START);
return ApiService
.get('general')
.then(({data}) => {
context.commit(FETCH_END);
context.commit(SET_GENERAL, data.general.results);
})
.catch(({response}) => {
context.commit(SET_ERROR, response.data.errors)
})
},
[ADD_GENERAL] (context, payload) {
context.commit(FETCH_START);
return ApiService
.postGeneral(`general`, '',payload)
.then(({data}) => {
context.commit(FETCH_END);
context.commit(SET_GENERAL, data.general.results);
})
.catch(({response}) => {
context.commit(SET_ERROR, response.data.errors)
})
}
};
const mutations = {
[FETCH_START] (state) {
state.loading = true
},
[FETCH_END] (state) {
state.loading = false
},
[SET_GENERAL] (state, pgeneral) { // can pass in payload
state.components = pgeneral;
state.errors = {}
},
[SET_ERROR] (state, errors) {
state.errors = errors
}
};
export default {
state,
getters,
actions,
mutations
}
This is module/index.js :
const requireModule = require.context('.', true, /\.js$/)
const modules = {}
requireModule.keys().forEach(fileName => {
if (fileName === './index.js') return
// Replace ./ and .js
const path = fileName.replace(/(\.\/|\.js)/g, '')
const [moduleName, imported] = path.split('/')
if (!modules[moduleName]) {
modules[moduleName] = {
namespaced: true
}
}
modules[moduleName][imported] = requireModule(fileName).default
})
export default modules
This is store/actions.js :
export const FETCH_GENERAL = "fetchGeneral";
export const ADD_GENERAL = "addGeneral";
This is store/index.js :
import Vue from 'vue'
import Vuex from 'vuex'
// Store functionality
import actions from './actions'
import getters from './getters'
import modules from './modules'
import mutations from './mutations'
import state from './state'
Vue.use(Vuex)
// Create a new store
const store = new Vuex.Store({
actions,
getters,
modules,
mutations,
state
})
export default store
This is store/mutations.js :
export const FETCH_START = "loadingOn";
export const FETCH_END = "loadingOff";
export const SET_ERROR = "setError";
// related to general
export const SET_GENERAL = 'setGeneral';
This is my vue-axios folder structure:
-services
-api.services.js
-config.js
This is services/api.serviecs.js :
import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
import { API_URL } from './config'
import Cookies from 'js-cookie'
let CSRF_TOKEN = Cookies.get('csrftoken');
export const ApiService = {
init () {
Vue.use(VueAxios, axios)
Vue.axios.defaults.baseURL = API_URL
},
get (resource, slug='') {
return Vue.axios
.get(`${resource}\${slug}`,)
.catch((error) => {
throw new Error(`ApiService ${error}`)
})
},
postGeneral (resource, slug='', obj) {
return axios
.post(`${API_URL}\\${resource}\\${slug}`,{
systemName: obj.systemName,
regionOfDeployment: obj.regionOfDeployment,
operatingMode: obj.operatingMode,
solution: obj.solution,
baselineMode: obj.baselineMode,
baselineDetails: obj.baselineDetails,
projectDuration: obj.projectDuration,
},
{
headers: {
'X-CSRFToken': CSRF_TOKEN,
'Content-Type': 'application/json',
}
})
.catch((error) => {
throw new Error (`ApiService ${error}`)
})
},
}
export default ApiService
This is config.js:
export default {}
export const API_URL = 'http://127.0.0.1:8000/api';
and finally this is my vuejs component:
...
<v-btn class="mt-5 mr-2 font-weight-light" color="blue"
#click="addGeneral" >
...
methods: {
addGeneral() {
let obj = {
systemName: '',
regionOfDeployment: '',
operatingMode: '',
solution: '',
baselineMode: '',
baselineDetails: '',
projectDuration: ''
};
this.postGeneral(obj)
},
postGeneral(obj) {
this.$store.dispatch(ADD_GENERAL, obj)
}
}
Why do I get the error and what's the best way to solve it?
You're using namespaced: true, so you need to pass module name in dispatch
postGeneral(obj) {
this.$store.dispatch('general/' + ADD_GENERAL, obj)
}
I've been using nuxtServerInit to fetch data from my contenful CMS and commit the mutation to add the data to the categories state object. However categories keeps showing empty, even when I try to render it on a component.
I can console.log the data in the mutation function and see the data, why isn't it being pushed into the categories state?
import Vuex from 'vuex'
import {createClient} from '~/plugins/contentful.js' //contentful plugin function
const client = createClient()
const createStore = () => {
return new Vuex.Store({
state: {
categories: {}
},
mutations: {
addCategories(state, data) {
state.categories += data.items
}
},
actions: {
async nuxtServerInit(context) {
client.getEntries({
content_type: 'category' //fetch everything with the content type set to category
})
.then(response => context.commit('addCategories', response))
.catch(console.error)
}
},
})
}
export default createStore
import Vue from 'vue'
import Vuex from 'vuex'
import { createClient } from '~/plugins/contentful'
const client = createClient()
export default = () => new Vuex.Store({
state: {
categories: {}
},
mutations: {
addCategories (state, data) {
Vue.$set(state, 'categories', [...data.items, ...state.catagories])
},
},
actions: {
async nuxtServerInit ({ commit }) {
let response
try {
response = await client.getEntries({ content_type: 'category' })
}
catch (error) {
console.log(error)
}
context.commit('addCategories', response)
},
})
I have my mutation committed, but the currentCampaign state is not updated instead returns undefined. Below is the screenshot.
This is the store.
import Vuex from 'vuex'
import Vue from 'vue'
import axios from 'axios'
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
campaigns: '',
currentCampaign: ''
},
actions: {
getCampaigns ( {commit} ) {
axios
.get('/api/campaign/history')
.then((response) => {
commit('GET_CAMPAIGNS', {campaigns: response.data});
})
}
},
mutations: {
GET_CAMPAIGNS: (state, {campaigns}) => {
state.campaigns = campaigns
},
getCurrentCampaign (state, {campaign}) {
state.currentCampaign = campaign
}
},
});
I am calling the mutation from component method like so:
methods: {
markAsCurrent (campaign) {
this.$store.commit('getCurrentCampaign', campaign.id)
}
}
What I'm not doing right here?
Thats why you are destructuring a campaign var and passing a number to the mutations, not a object.
Try this
getCurrentCampaign (state, campaign) {
state.currentCampaign = campaign
}
Why do my promises not actually update the state in Redux?
I'm using redux-promise-middleware. When I make a call to my API, it goes through the promise steps of _PENDING and _FULFILLED, but the state is never updated to reflect the changes.
How do I do this properly, so that I actually get my data.
Here's a picture of my state:
As you can see, isFetched does not become true after the promise is fulfilled, and data is never loading the returned response data into itself.
This is my API helper:
class UserAPI {
...
async testPhone(user) {
await axios.post(this.testPhonePath, {
phone: user.phone
})
.then(function(response) {
return response.data
})
.catch(function(error) {
return error.response.data
})
}
}
My action:
import { UserAPI } from '../../constants/api'
const userAPI = new UserAPI()
export const TEST_USER_PHONE = 'TEST_USER_PHONE'
export const testUserPhone = (user) => ({
type: TEST_USER_PHONE,
payload: userAPI.testPhone(user)
})
And my reducer:
import {
TEST_USER_PHONE
} from './actions'
const INITIAL_STATE = {
testedByPhone: {
data: [],
isFetched: false,
error: {
on: false,
message: null
}
}
}
export default (state = INITIAL_STATE, action) => {
switch(action.type) {
case '${TEST_USER_PHONE}_PENDING':
return INITIAL_STATE
case '${TEST_USER_PHONE}_FULFILLED':
return {
testedByPhone: {
data: action.payload,
isFetched: true,
error: {
on: false,
message: null
}
}
}
case '${TEST_USER_PHONE}_REJECTED':
return {
testedByPhone: {
data: [],
isFetched: true,
error: {
on: true,
message: action.payload
}
}
}
default:
return state
}
}
Here's my Store
import { createStore, applyMiddleware, compose } from 'redux'
import promiseMiddleware from 'redux-promise-middleware'
import reducers from './reducers'
const middleware = [
promiseMiddleware()
]
if (__DEV__) {
const logger = require('redux-logger')
middleware.push(logger())
}
const enhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
export default createStore(
reducers,
undefined,
enhancers(applyMiddleware(...middleware))
)
The reason it isn't working, it is that you use a standard string instead of JS templates.
Replace:
'${TEST_USER_PHONE}_REJECTED'
With:
`${TEST_USER_PHONE}_REJECTED`
I suspect you wanted to use either
testPhone(user) {
return axios.post(this.testPhonePath, {
phone: user.phone
}).then(function(response) {
return response.data
}, function(error) {
return error.response.data
});
}
or
async testPhone(user) {
try {
const response = await axios.post(this.testPhonePath, {
phone: user.phone
});
return response.data
} catch(error) {
return error.response.data
}
}
but not that current mix which always returns a promise for undefined - it only uses await but not return.