Vuex getter wasn't updated after store mutation - javascript

I've created app using VueJS and Vuex.
This code is used in store:
const separateArticle = {
state: {article: {}},
mutations: {
LOAD_ARTICLE(state, id) {
fetch(`http://localhost:3000/articles/${id}`, { mode: "cors" })
.then(resp => resp.json())
.then(response => {
state.article = response;})}
},
actions: {
loadSeparateArticle({ commit }, id) {commit("LOAD_ARTICLE", id);}
},
getters: { articleData(state) {return state.article;} }
};
component code :
<template>
<div>
<img src="articleData.imgSrc"/>
<Article v-bind:article = "articleData" v-bind:key="articleData._id"></Article>
<div>
Comments
</div>
</div>
</template>
<script>
import Article from "./Article.vue";
export default {
props: ["article"],
name: "ArticleDetail",
components: {
Article
},
data() {
return { articleData: this.$store.getters.articleData };
},
created() {
this.$store.dispatch("loadSeparateArticle", this.$route.params._id);
}
};
</script>
When the component is created getter is triggered, but after store modification, it wasn't called. Vue doesn't understand that store is updated.
How can I manage this issue?

Related

Vuex state not updating with mutation

I'm trying to change the value of a state from false to true but it's not seeming to work. Below I will add my store, component that only gets the state, and the component that should update the state.
//Store
export const store = new Vuex.Store({
state: {
newComment: false,
status: ''
},
mutations: {
updateComments(state, payload) {
state.newComment = payload;
}
}
});
// Component that gets state
<template>
<div class="home">
<h1>{{newComment}}</h1>
<SelectedVideo v-bind:user="user" v-bind:video="videos[0]"/>
</div>
</template>
<script>
import axios from 'axios';
import SelectedVideo from '../components/SelectedVideo.component';
axios.defaults.withCredentials = true;
export default {
name: 'Home',
components: {
SelectedVideo
},
data() {
return {
videos: [],
user: null
}
},
computed: {
newComment() {
return this.$store.state.newComment
}
},
// Component I need to update state
methods: {
updateComments() {
this.$store.dispatch('updateComments', true);
}
},
async createComment(comment, video, user) {
try{
const res = await axios({
method: 'POST',
url: 'http://localhost:8000/api/v1/comments/',
data: {
comment,
video,
user
}
});
if (res.data.status === 'success') {
console.log(res);
// location.reload(true);
this.updateComments();
}
} catch(err) {
I'm successfully getting the state but updating seems to have no affect on state when I try to invoke the function to update.
First make a getter for using state data.
getters:{
get_newComment: state => state.newComment
}
Than implement this into your computed property.
computed: {
newComment() {
return this.$store.getters.get_newComment
}
}
After then use to "Commit" to commit some changes not "dispatch". Dispatch using for calling actions.

'unknown action type' with Vuex mapAction

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',
]),
},

How to update Vuex store correctly?

I'm working on the time-honored Todo list feature using Vue, Vuex, and axios. I have the feature componentized between the
whole list itself (TodoList) and the individual rows (TodoRow).
Please note that I have abbreviated the code snippets below to eliminate properties and elements that are not essential to understanding this problem. I would not recommend running the code locally.
TodoList component
<template>
<dashboard-container
:header-title="'To-Do-List'"
>
<div slot="action-buttons">
<add-new-button
v-if="!emptyTodos"
:disabled="isCreating"
class="pull-right"
#click="addNewTodo"
/>
</div>
<transition slot="container-content" tag="div">
<div v-if="!emptyTodos">
{{ todos }}
<todo-row
v-for="(item, index) in todos"
:key="index" :id="item.id"
:text="item.description"
:status="item.status"
:completed="item.completed"
/>
</div>
<empty-list v-else #addClick="addNewTodo" />
</transition>
</dashboard-container>
</template>
<script>
import DashboardContainer from '#/components/Dashboards/DashboardContainer'
import TodoRow from '#/components/TodoList/TodoRow'
import AddNewButton from './AddNewButton'
import EmptyList from './EmptyList'
import { mapState, mapActions } from 'vuex'
export default {
components: {
DashboardContainer,
EmptyList,
TodoRow,
AddNewButton
},
computed: {
...mapState('planProfile', {
todos: 'todos'
}),
emptyTodos () {
return this.todos.length === 0
},
isCreating () {
return this.todos.some(todo => todo.status === 'created')
}
},
mounted () {
return this.dispatchAction({
actionName: 'planProfile/getTodos'
}).catch(err => {
console.log('decide how to route handle errors', err)
})
},
methods: {
...mapActions({
dispatchAction: 'dispatchAction'
}),
addNewTodo () {
this.todos.unshift({
text: '',
done: false,
status: 'new'
})
}
}
}
</script>
Here is what the feature (with the non-abbreviated code) looks like in my browser with a few todo items. I have dumped the todos just for
easy illustration of the raw data in the store.
Here is the abbreviated code for the todo item itself:
TodoItem component
<template>
<div #mouseover="showActions = true" #mouseout="showActions = false">
<div>
<div>
<label>
<input
:checked="done"
type="checkbox"
>
</label>
</div>
<div v-show="showActions">
<div
v-for="action in currentActions"
:key="action.name"
class="action-icon pull-right"
#click="handleActionClick(action.name)"
>
<svg-icon :svg-settings="action.icon" />
</div>
</div>
</div>
</div>
</template>
<script>
import makeMdsIcon from '#/utils/makeMdsIcon'
import CheckboxInput from '#/components/Inputs/CheckboxInput'
import { mapActions } from 'vuex'
export default {
components: {
CheckboxInput
},
props: {
id: {
type: Number,
default: null
},
status: {
type: String,
default: 'created'
},
completed: {
type: Boolean,
default: false
}
},
data () {
return {
showActions: false,
checkIcon: makeMdsIcon('check--s')
}
},
computed: {
done () {
return this.completed
},
isCreating () {
return this.status === 'new' || this.status === 'updating'
},
currentActions () {
const actions = {
new: [{
name: 'save',
icon: this.checkIcon
}]
}
return actions[this.status]
}
},
created () {
if (this.completed) {
this.status = 'done'
}
},
methods: {
...mapActions({
dispatchAction: 'dispatchAction'
}),
saveTodo () {
this.status = 'created'
return this.dispatchAction({
actionName: 'planProfile/createTodo',
data: {
description: this.text
}
}).catch(err => {
console.log('decide how to route handle errors', err)
})
},
handleActionClick (actionName) {
const handlers = {
save: this.saveTodo
}
const currentHandler = handlers[actionName]
return currentHandler()
}
}
}
</script>
When the saveTodo method is called and the underlying planProfile/updateStatusTodo action is called at the Vuex store level, the save persists on the backend and I can verify this when I refresh. However, as we can see from the TodoList component, the store is not updated when this happens:
Abbreviated Vuex store
import todoService from '#/services/todoService'
const state = {
todos: []
}
const actions = {
createTodo: {
handler ({ commit }, payload = []) {
const { description } = payload
return todoService.createTodo({ description })
.then(todoData => {
return commit('addTodo', todoData)
})
}
}
}
const mutations = {
addTodo (state, payload) {
/* The line below is just for demonstrative purposes for this question */
state.todos = state.todos.push({id: 9999, description: 'this is mock data for SO', completed: false})
}
}
export default {
namespaced: true,
state,
actions,
mutations
}
Why is the store not being updated when addTodo is called at the Vuex store level? I have already validated the procedure
for other todo operations (such as deleting a todo) whereby I delete the todo and this information is instantaneously communicated to the
TodoList component, why is the same not happening for creating new todos?
Flow:
Right before I am about to save via clicking on the checkmark next to the first item:
After clicking, item has been saved on the backend (which I can verify by refreshing), but it's not in the store since the todos have not changed (and also by inspection of the Vuex store in the Chrome developer console).

Pass a state of vuex to a chart - vue

I'm doing a chart component that will be several times represented on a page.
For that, I'm using Vuex where I have the mutations / actions to get that date, and everything works fine.
In the graph component, in created () I call the action to make the request, and I want to pass the date to the chart.
The state comes as follows -
{
typeChart: [data],
typeChart2: [data2]
}
If I do console.log from the state it appears fine but if using the key returns undefined.
console.log(this.dataChart)
// log - { stacked: Array }
console.log(this.dataChart.stacked);
// log - undefined
The only way I found to pass the state to the chart was to do a timeout but I think that is not the best way to do this.
here's the code I have
component of page -
<template>
<stacked-chart
endpoint="endpoint"
chart="stacked"
:states="['state1', 'state2', 'state3']"
:keys="['key1', 'key2', 'key3']"
/>
</template>
component of chart -
<template>
<div class="box-content-white">
<div class="title"> Gráfico 1 </div> <!-- //! name for chart -->
<column-chart :data="data" :stacked="true" :colors="['#006837', '#FF0000', '#000']"></column-chart>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import { mapActions } from 'vuex';
export default {
props: {
endpoint: String,
chart: String,
states: Array,
keys: Array
},
data() {
return {
data: []
}
},
created() {
this.fetchReporting({endpoint: this.endpoint, chart: this.chart, states: this.states, keys: this.keys, formData: { state: 'negotiation' }});
console.log(this.dataChart);
console.log(this.dataChart[this.chart]);
setTimeout(() =>{
this.data = this.dataChart[this.chart];
}, 150);
},
methods: {
...mapActions({
fetchReporting: 'fetchReporting'
})
},
mounted() {
this.data = this.dataChart[this.chart];
},
computed: {
...mapGetters({
dataChart: 'dataChart'
})
},
watch: {
}
}
</script>
file with vuex -
import axios from 'axios';
const state = {
dataChart: {}
}
const mutations = {
'ADD_DATA_CHART'(state, data) {
state.dataChart[data.key] = [];
[].forEach.call(data.states, (s, i) => {
let obj = {};
obj.name = s;
obj.data = [];
[].forEach.call(data.value, d => {
obj.data.push([d.name, d[data.keys[i]].toFixed(2)]);
});
state.dataChart[data.key].push(obj);
});
}
}
const actions = {
fetchReporting({state, commit}, response) {
axios.post(response.endpoint, response.formData)
.then(({data}) => {
commit('ADD_DATA_CHART', {key: response.chart, value: data, states: response.states, keys: response.keys})
}).catch(err => {
console.log(err);
});
}
}
const getters = {
dataChart: state => {
return state.dataChart;
}
}
export default {
state,
mutations,
actions,
getters
}
any suggestion?
You need to wait for you action fetchReporting to complete. Either by using async/await or by using promise/resolve

Pass value to vuex store from mapActions in component

How would I access the value inside my #click partSelect from my Vuex store.
Vue Template
<div id="currentPart">
<img id="modelImg" #click="partSelect('modelOne')" class="modelImg" :src="model[0]"/>
<img id="modelImg2" #click="partSelect('modelTwo')" class="modelImg" :src="model[1]"/>
<img id="modelImg3" #click="partSelect('modelThree')" class="modelImg" :src="model[2]"/>
</div>
Javascript to map actions
<script>
methods: mapActions([
'getImage',
'getImageAsync',
'partSelect'
]),
</script>
Vuex Javascript
export const partSelect = ({ commit }) => commit('partSelect')
This is actually pretty simple, the action in Vuex should look like this:
export const partSelect = ({ commit }, myString) => {
// do something with myString
commit('partSelectMutation') // execute the mutation
// if the data should be added to the mutation:
// commit('partSelectMutation', { myString })
}
to access the variable myString in the mutation (if the second version above was used):
mutations: {
partSelectMutation: (state, { myString }) => {
state.myString = myString
},
//...
}
Simple example with mapped action as event callback:
var store = new Vuex.Store({
state: {
content: 'Old content'
},
mutations: {
updateContent (state, payload) {
state.content = payload
}
},
actions: {
mapMe (context, payload) {
context.commit('updateContent', payload)
}
}
})
new Vue ({
el: '#app',
store,
computed: {
content () {
return this.$store.state.content
}
},
methods: Vuex.mapActions({
mappedAction: 'mapMe'
})
})
<div id="app">
{{ content }}<br>
<button #click="mappedAction('New content')">Click</button>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>
This worked for me:
const mutations = {
myrequest (state, id) {
console.log("id:" + id);
}
}
const actions = {
myrequest ({ commit }, id) {
commit('myrequest', id)
}
}

Categories