im beginner and trying something for learn. im using vue js 2 and have a problem with vuex. When I click div of dropdown, I want to add active class and show content of dropdown. The problem is
unknown mutation type: dropdownState.
store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
active: false,
},
getters: {
isOpen (state) {
return state.active;
},
},
mutation: {
dropdownState (state) {
state.active = !state.active;
console.log('hi');
},
},
});
Dropdown.vue
<template>
<div class="dropdown" #click="dropdownState">
<slot name="button"></slot>
<slot></slot>
</div>
</template>
<script>
import { mapMutations } from 'vuex';
export default {
name: 'Dropdown',
methods: {
...mapMutations(['dropdownState']),
}
};
</script>
DropdownContent.vue
<template>
<div>
<div class="dropdown-content right" :class="{ active : active }"></div>
</div>
</template>
<script>
import { mapState, mapGetters } from 'vuex';
export default {
name: 'DropdownContent',
computed: {
...mapState(['active']),
...mapGetters(['isOpen']),
},
};
</script>
It's supposed to be mutations (with an s)
mutations: {
...
}
https://vuex.vuejs.org/guide/mutations.html#mutations
Btw, it will be actions too!
Related
I am trying to learn vuex and following tutorials on youtube for vuex, I am in a error of
TypeError: Cannot read properties of undefined (reading 'state'), where I am not able figure out why I am getting this error , I assume it might be because of the newer version not sure.
Here is my code:
TodoList.vue
<template>
<div>
<h4>Todo list</h4>
<todo
v-for="(task, index) in $store.state.todos" // getting error here
:key="index"
:todo="todo"
></todo>
</div>
</template>
<script>
import Todo from "./Todo.vue";
export default {
name: "TodoList",
components: { Todo },
computed: {},
};
</script>
<style>
</style>
here is my
store/index.js
import { createStore } from 'vuex';
export const store = createStore({
state: {
todos: [{
title: 'Task 1',
completed: false
},
{
title: 'Task 2',
completed: false
}
]
}
// other stuff
});
mapState is a helper that simplifies creating a computed property that reflects the value of a given state
You can access your data like below:
<template>
<div>
<h4>Todo list</h4>
<todo
v-for="(task, index) in todos"
:key="index"
:todo="todo"
></todo>
</div>
</template>
import { mapState } from 'vuex'
export default {
data: {},
computed: {
...mapState({
todos: state => state.todos
})
}
}
I am trying to make a simple project which has 2 Vue pages add.vue and read.vue. If we add a message in add.vue(using commit to mutate teh state), results should be displayed in read.vue , using Vuex store. (I am using Nuxt)
store/index.js file=>
export const state = () => ({
messages:[]
})
export const mutations={
addMessage:(state, comment)=> {
state.messages.push(comment)
}
}
add.vue file
<template>
<div class="addContainer">
<b-form #submit.prevent="handleSubmit">
<b-form-group id="input-group-2" label="Comment:" label-for="input-2">
<b-form-input
id="input-2"
v-model="comment"
required
placeholder="Enter Your Comment"
></b-form-input>
</b-form-group>
<b-button type="submit" variant="primary">Submit</b-button>
</b-form>
</div>
</template>
<script>
import Header from '~/components/Header'
import {mapActions} from 'vuex'
import {mapState} from 'vuex'
export default {
data() {
return {
comment:''
}
},
computed:{
...mapState([
'messages'
])
},
methods:{
handleSubmit(){
this.$store.commit('addMessage', this.comment)
console.log('Messages is '+this.messages )
this.comment = ''
}
},
components:{
Header
}
}
</script>
read.vue
<template>
<div>
<p>Read Messages</p>
<ul>
<li v-for="(msg, index) in messages" :key="index">
<b>{{ msg}}</b>
<br>
{{ messages}}
</li>
</ul>
</div>
</template>
<script>
import {mapState} from 'vuex'
export default {
data() {
return {}
},
computed:{
...mapState({
messages:state=>state.messages
})
}
}
</script>
In Vue component , I can see messages array inside state changes after adding a message , but same changes are not reflected in read page.
Add getters to your store:
export const getters= {
getMessages: state => state.messages
}
and then use the getters in your component instead of accessing the state directly:
// read.vue
<script>
import {mapGetters} from 'vuex'
export default {
data() {
return {}
},
computed:{
...mapGetters({
messages: 'getMessages',
})
}
}
</script>
After fullcalendar updated their props to go outside of the <FullCalendar /> element I have some problems rendering events.
I know that the state request is fulfilled in Vuex because I've tested this so many this without a getter and with a getter and nothing seems to work...
<template>
<v-container>
<FullCalendar :options="calendarOptions" />
<li v-for="event in events" :key="event.id">
{{event.name + ' ' + event.start}}
</li>
</v-container>
</template>
<script>
import FullCalendar from '#fullcalendar/vue'
import dayGridPlugin from '#fullcalendar/daygrid'
import interactionPlugin from '#fullcalendar/interaction'
import timeGridPlugin from '#fullcalendar/timegrid'
import listPlugin from '#fullcalendar/list'
import { mapState, mapActions, mapGetters } from 'vuex'
export default {
components: {
FullCalendar
},
data() {
return {
calendarOptions: {
plugins: [timeGridPlugin, listPlugin ,dayGridPlugin, interactionPlugin ],
initialView: 'dayGridMonth',
selectable: true,
events: this.allEvents,
}
}
},
computed: {
...mapState({
events: state => state.calendar.events
}),
...mapGetters('calendar', ['allEvents']),
},
created () {
this.getAll();
},
methods: {
...mapActions('calendar', {
getAll: 'getAll',
}),
}
}
</script>
Is there any way to render the elements with just the state? I thought that setting events to this.events would be working, but I'm probably way off here. I'm new to Vue and Vuex so help is much appreciated! :) The list is just to know that the state is working as it should be.
You can still bind the events in template by doing something like this:
<FullCalendar :options="{
...calendarOptions,
events: events
}" />
I am developing a single-page-application using vue-cli3 and npm.
The problem: Populating a basic integer value (stored in a vuex state) named counter which was incremented/decremented in the backend to the frontend, which displays the new value.
The increment/decrement mutations are working fine on both components (Frontend/Backend), but it seems like the mutations don't work on the same route instance: When incrementing/ decrementing the counter in backend, the value is not updated in the frontend and otherwise.
store.js:
Contains the state which needs to be synced between Backend/Frontend.
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
counter: 10
},
mutations: {
increment (state) {
state.counter++
},
decrement (state) {
state.counter--
}
}
})
index.js:
Defines the routes that the vue-router has to provide.
import Vue from 'vue'
import Router from 'vue-router'
import Frontend from '#/components/Frontend'
import Backend from '#/components/Backend'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Frontend',
component: Frontend
},
{
path: '/backend',
name: 'Backend',
component: Backend
}
],
mode: 'history'
})
main.js:
Inits the Vue instance and provides the global store and router instances.
import Vue from 'vue'
import App from './App'
import router from './router'
import { sync } from 'vuex-router-sync'
import store from './store/store'
Vue.config.productionTip = false
sync(store, router)
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
Frontend.vue/Backend.vue:
Both (Frontend/Backend) use the same code here.
They use the state counter in order to display and modify it.
<template>
<div> Counter: {{ getCounter }}
<br>
<p>
<button #click="increment">+</button>
<button #click="decrement">-</button>
</p>
</div>
</template>
<script>
export default {
name: 'Frontend',
methods: {
increment () {
this.$store.commit('increment')
},
decrement () {
this.$store.commit('decrement')
}
},
computed: {
getCounter () {
return this.$store.state.counter
}
}
}
</script>
It would be awesome if someone sould tell me what I am missing or if I have misunderstood the concept of vuex and vue-router.
Just get the counter from the store for both components. You don't need data as store is already reactive.
<template>
<div> Counter: {{ counter }}
<br>
<p>
<button #click="increment">+</button>
<button #click="decrement">-</button>
</p>
</div>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
export default {
name: 'Frontend',
methods: {
...mapMutations([
'increment',
'decrement',
])
},
computed: {
...mapState({
counter: state => state.counter,
})
}
}
</script>
For reference:
mapState: https://vuex.vuejs.org/guide/state.html#the-mapstate-helper
mapMutations: https://vuex.vuejs.org/guide/mutations.html#committing-mutations-in-components
#sebikolon component properties that are defined in data () => {} are reactive, methods are not, they are called once. Instead of {{ getCounter }}, just use {{ $store.state.counter }}. OR initiate property in each component that gets the value of your state.
data: function () {
return {
counter: $store.state.counter,
}
}
My component does not update the loaded property when Store.loaded changes:
Component
import { Vue } from 'vue-property-decorator'
import Component from 'nuxt-class-component'
import { Store } from '../repositories'
#Component
export default class Layout extends Vue {
loaded = Store.loaded
}
Store
class Root {
loaded = false
}
export let Store = new Root()
export default Store
In your example Store is just plain function (class), without any reactivity (no Vue watchers attached for Store.loaded field).
Only properties inside component's data are reactive. If you want reactive single store outside of vue components (better for big frontend applications), you should use Vuex
Simple example will be:
App.vue:
<script>
import { mapGetters, mapMutations } from 'vuex';
import store from './store';
import ChildComponent from './components/ChildComponent.vue';
export default {
store,
components: { ChildComponent },
methods: {
...mapMutations(['toggleLoaded']),
},
computed: {
...mapGetters({
isLoaded: 'isLoaded',
}),
}
}
</script>
<template>
<div id="app">
Toggle loaded
<h3>Root component: </h3>
<div>The loaded flag is: {{ isLoaded }}</div>
<ChildComponent />
</div>
</template>
components/ChildComponent.vue:
<script>
import { mapGetters } from 'vuex';
export default {
computed: {
...mapGetters({
isLoaded: 'isLoaded', //accessing to same data, as root through single Vuex state
}),
}
}
</script>
<template>
<div class="hello">
<h3>Child component</h3>
<div>The loaded flag is: {{ isLoaded }}</div>
</div>
</template>
And reactive Vuex store:
store/index.js:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const state = {
loaded: false
};
const getters = {
isLoaded: state => state.loaded,
};
const mutations = {
toggleLoaded: (state) => {
state.loaded = !state.loaded;
}
};
export default new Vuex.Store({
state,
mutations,
// actions,
getters,
strict: true
});
You can find full source of this example on GitHub.