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
Related
I am trying to solve a problem in my vuex store. I write two different actions in my store. One action is reactive and the other not. But I need the loadSlidesEach() in reactivity, so the data are updated. I cant find the mistake.
My store:
const store = new Vuex.Store({
state: {
loadedSlides: []
},
mutations: {
setSlides(state, slides) {
state.loadedSlides = slides
},
setSlidesPush(state, slide) {
state.loadedSlides.push(slide)
}
},
getters: {
loadedSlides(state) {
return state.loadedSlides
}
},
actions: {
loadSlides({ commit, getters, state }) {
firebase.database().ref('/slides/').on('value', snapshot => {
const slidesArray = []
const obj = snapshot.val()
for (const key in obj) {
slidesArray.push({ ...obj[key], id: key })
}
commit('setSlides', slidesArray)
})
},
loadSlidesEach({ commit, getters, state }, id) {
firebase.database().ref('/slides/' + id).on('value', snapshot => {
const slide = snapshot.val()
commit('setSlidesPush', { ...slide })
})
}
}
})
My component 1: Array from slides() is reactive
export default {
computed: {
slides() {
return this.$store.getters.loadedSlides
}
},
created() {
this.$store.dispatch('loadSlides')
}
}
My component 2: Array from slides() is not reactive
export default {
computed: {
slides() {
return this.$store.getters.loadedSlides
}
},
created() {
const slides = ['1','2','3']
for (let i = 0; i < slides.length; i++) {
this.$store.dispatch('loadSlidesEach', slides[i])
}
}
}
I think the problem is something with the inital state or the mutation with push(). Any advices?
Update:
The two actions are only different in the mutations. So what is the best way to set the state in vuex? The store get confused if I call the action loadSlidesEach() in a loop.
Don't use await and then together. Use one or another:
loadSlidesEach: async({ commit, getters }, id) => {
const data = await firebase.database().ref('/slides/' + id).once('value')
const slide = data.val()
commit('setSlidesPush', { ...slide })
}
do you try to use mapState from vuex ?:
import {mapState} from 'vuex'
export default {
computed: {
...mapState(['loadedSlides '])
}
}
now you can use loadedSlides in component.
I found a working solution for my problem.
Change the mutation:
setSlidesPush(state, addedSlide) {
const slideIndex = state.loadedSlides.findIndex(slide => slide.id === addedSlide.id)
if (slideIndex !== -1) {
Vue.set(state.loadedSlides, slideIndex, addedSlide)
} else {
state.loadedSlides.push(addedSlide)
}
}
I was able to fetch data and display them using Nuxt's Fetch API, but I want to utilize Vuex instead.
store/index.js:
import Axios from 'axios'
export const getters = {
isAuthenticated (state) {
return state.auth.loggedIn
},
loggedInUser (state) {
return state.auth.user
}
}
export const state = () => ({
videos: []
})
export const mutations = {
storeVideos (state, videos) {
state.videos = videos
}
}
export const actions = {
async getVideos (commit) {
const res = await Axios.get(`https://api.themoviedb.org/3/movie/popular?api_key=${process.env.API_SECRET}&page=${this.currentPage}`)
commit('storeVideos', res.data)
}
}
pages/test.vue:
<template>
<Moviecards
v-for="(movie, index) in $store.state.videos"
:key="index"
:movie="movie"
:data-index="index"
/>
</template>
<script>
...
fetch ({ store }) {
store.commit('storeVideos')
},
data () {
return {
prevpage: null,
nextpage: null,
currentPage: 1,
pageNumbers: [],
totalPages: 0,
popularmovies: []
}
},
watch: {
},
methods: {
next () {
this.currentPage += 1
}
}
}
...
The array returns empty when I check the Vue Dev Tools.
In fetch(), you're committing storeVideos without an argument, which would set store.state.videos to undefined, but I think you meant to dispatch the getVideos action:
export default {
fetch({ store }) {
// BEFORE:
store.commit('storeVideos')
// AFTER:
store.dispatch('getVideos')
}
}
Also your action is incorrectly using its argument. The first argument is the Vuex context, which you could destructure commit from:
export const actions = {
// BEFORE:
async getVideos (commit) {} // FIXME: 1st arg is context
// AFTER:
async getVideos ({ commit }) {}
}
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.
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'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).