Vuex filter state - vuejs2

I'm at my first app in Vuejs Vuex.
I can not find the best way to filter a state.
store/index.js
state: {
projects: []
},
mutations: {
SET_PROJECT_LIST: (state, { list }) => {
state.projects = list
}
},
actions: {
LOAD_PROJECT_LIST: function ({ commit }) {
axios.get('projects').then((response) => {
commit('SET_PROJECT_LIST', { list: response.data})
}, (err) => {
console.log(err)
})
}
}
in the component:
computed: {
...mapState({
projects
})
}
At this point I have a list of my projects. Good!
Now I added buttons to filter my projects like:
Active Project, Type Project ...
How do I manipulate my projects object (this.projects)?
With another one this.$store.dispatch
With another getters function
I manipulate the state without changing the status?
I'm a bit confused.
Some examples of filters on lists populated in Vuex?
EDIT:
I was trying that way:
this.$store.getters.activeProjects()
But how I update this.projects?
activeProjects(){
this.projects = this.$store.getters.activeProjects()
}
does not work

I'd recommend to keep your original state intact and filter its data by using "getters".
In fact, the official documentation includes an example of how to get all the "done" todos. It might be useful for you:
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
Getters reference: https://vuex.vuejs.org/en/getters.html
Good luck!

Related

I want to change count value by using v-model in vue app in vuex store

This code does not generate error but when I change my count it does not show the result on screen. Kindly help me in resolving the issue.
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
lists: [
{
title: "User",
count: 15,
},
{
title: "Admin",
count: 41,
},
{
title: "Total Members",
count: 100,
},
{
title: "Manager",
count: 35,
}
]
},
mutations: {
updateMessage (state, count) {
state.lists.count = count
}
},
actions: {
},
modules: {
}
})
First thing, you don't need v-model to update your store. You can create a local copy and update it on the fly.
Second thing, I think you don't want to update not the whole counts of every objects but I guess you want to update an item in particular.
That's what I would do:
// 1. In component you watch state.lists and copy it immediately on init, deeply & on change:
data() {
return {
localCopyOfLists: []
}
},
watch: {
state.lists: {
immediate: true,
deep: true,
handler(v) {
this.localCopyOfLists = this.state.lists.map(x => (x))
}
}
}
// 2. Methods to change count of element in local list.
methods: {
updateItemInArray(index, count) {
this.localCopyOfLists[index].count = count
this.store.dispatch('SAVE_NEW_ARRAY', this.localCopyOfLists)
}
}
// 3. You update your store.
import Vue from 'vue'
export default new Vuex.Store({
actions: {
SAVE_NEW_ARRAY ({commit}, payload) {
commit('UPDATE_ARRAY', payload)
}
},
mutations: {
UPDATE_ARRAY (state, payload) {
Vue.set(state, lists, payload)
}
}
})

How do I implement a filter on a data returned from the store in Vue?

I have a .js file with the following:
Vue.mixin({
data() {
return {
searchFilterSetupBase: {
BasisMasterIdEx: {
Title: this._rsc('Basis', 'Basis'),
Component: 'CheckboxListSelect',
SingleSelect: true,
Source: this.$store.state.storage.lists.BasisMasters//,
}
}
}
},
This works returning the data from the store.
However, I want to filter this data but can't get it to work.
I have tried replacing Source: this.$store.getters.getBasisMasterById() and using a getter as below:
getters: {
getBasisMasterById: state => {
if (this.$store.state.storage.lists.BasisMasters) {
return this.$store.state.storage.lists.BasisMasters.filter((basisMaster) => {
return basisMaster.t === ('STAT')
})
}
}
},
I tried to do something like https://vuex.vuejs.org/guide/getters.html#property-style-access
but just dont know how to implement it. Please help
Editing my post.
I tried the following with getters:
Source: this.$store.getters['movies']
getters: {
movies: state => this.$store.state.storage.lists.BasisMasters
},
computed: {
movies() {
alert('aaaa');
return state.getters['movies']
},

Vuex mapState based on route and route parameters

I have a works component I use different pages on my app and I am trying to load the state based on the route and route parameters.
In my App.vue file I dispatch the async action to get the json file like
mounted() {
this.$store.dispatch('getData')
},
And I map the state in my works component like that
export default {
name: 'Works',
computed: mapState({
works: (state) => state.works.home.items.slice(0, state.works.home.loadedCount),
loadedCount: (state) => state.works.home.loadedCount,
totalCount: (state) => state.works.home.items.length,
})
}
I actually need to map the state dynamically based on the route just like state.works[this.$router.currentRoute.params.category] or based on route name.
Could you please tell me what is the correct way to get the data (async) from my state?
Vuex store:
export default new Vuex.Store({
state: {
works: {
all: {
items: [],
loadedCount: 0,
},
home: {
items: [],
loadedCount: 0,
},
web: {
items: [],
loadedCount: 0,
},
print: {
items: [],
loadedCount: 0,
},
},
limit: 2,
},
mutations: {
SET_WORKS(state, works) {
state.works.all.items = works
works.map((el) => {
if (typeof state.works[el.category] !== 'undefined') {
state.works[el.category].items.push(el)
}
})
},
},
actions: {
getData({ commit }) {
axios
.get('/works.json')
.then((response) => {
commit('SET_WORKS', response.data.works)
})
},
},
})
You can do it in beforeCreate hook.
beforeCreate(){
const category = this.$route.params.category;
Object.assign(this.$options.computed, {
...mapState({
categoryItems: (state) => state.categories[category],
}),
});
}
I've created a basic working example: https://codepen.io/bgtor/pen/OJbOxKo?editors=1111
UPDATE:
To get mapped properties updated with route change, you will have to force re-render the component. The best way to do it, is to change the component key when route change in parent component.
Parent.vue
<template>
<categoryComponent :key="key"></categoryComponent> // <-- This is the component you work with
</template>
computed: {
key(){
return this.$route.params.category
}
}
With this approach the beforeCreate hook will be triggered with every route change, getting fresh data from Vuex.

how to debug mapGetter property in Vue.js component?

I am new to Vue.js. I have recently learned Vuex and trying to implement in my project.
I am calling calling an action dispatch from my dashboard component. And calling ...mapGetter in message component computed section. And I want to debug the data that I am getting.
I already searched my problem. But couldn't find it. What I learned I can't use console.log() in computed. I have to use debugger. But when I am using debugger it's saying debugger is a reserved word.
in my store:
state: {
conversationThreads: [],
conversation: [],
users: [],
},
getters: {
conversation: state => {
return state.conversation;
}
},
mutations: {
[MUTATION_TYPES.SET_CONVERSATION](state, conversationThread){
state.conversation= conversationThread;
}
},
actions: {
getConversationByID: ({ commit }, conversationInfo) => {
console.log("conversationData: ", conversationInfo)
axios.get("https://some_API" + conversationInfo.id)
.then(response => {
let conversationThread = response.data.messages.data.map(res => ({
name: res.from.name,
msg: res.message
}));
commit(MUTATION_TYPES.SET_CONVERSATION, conversationThread);
})
.catch(error => console.log(error))
}
}
in my dashboard component:
methods: {
selectedDiv: function(conversationInfo, event){
this.$store.dispatch('getConversationByID', conversationInfo)
}
}
in my message component:
computed: {
...mapGetters([
"conversation"
]),
debugger
},
You can get similar functionality without using mapGetter, below is example.
computed: {
yourProperty(){
const profile = this.$store.getters.profile;
console.log('profile: ', profile); //Debug
return profile;
}
},
Another option is to put a watch on computed property.
computed: {
...mapGetters(["profile"]),
},
watch: {
profile: {
handler(profile) {
console.log('profile: ', profile); //Debug
},
deep: true
}
},
Here deep true option is used to watch on key updates of profile object. If deep true is not provided then watch will get called only when profile getter is reassigned with new object.

How do I set initial state in Vuex 2?

I am using Vue.js 2.0 and Vuex 2.0 for a small app. I am initializing the store in the 'created' life-cycle hook on the root Vue instance by calling an action that retrieves the initial state from an API....like so in my Root Component:
const app = new Vue({
el: "#app",
router,
store,
data: {
vacation: {},
},
components: {
'vacation-status': VacationStatus,
},
created() {
//initialize store data structure by submitting action.
this.$store.dispatch('getVacation');
},
computed: {},
methods: {}
});
This is working just fine. Here is the action on my store that I'm calling here:
getVacation({ commit }) {
api.getVacation().then(vacation => commit(UPDATE_VACATION, vacation))
}
The mutation that this is committing with 'UPDATE_VACATION' is here:
[UPDATE_VACATION] (state, payload) {
state.vacation = payload.vacation;
},
My Problem: When I load the app, all my components that are 'getting' values from the store throw errors I'm trying to access 'undefined' values on the store. In other words, state hasn't been initialized yet.
For example, I have a component that has getters in Child Components like this:
computed: {
arrival() {
return this.$store.getters.arrival
},
departure() {
return this.$store.getters.departure
},
countdown: function() {
return this.$store.getters.countdown
}
}
All these getters cause errors because 'vacation' is undefined on the state object. It seems like an asynchronous problem to me, but could be wrong. Am I initializing my store state in the wrong spot?
Vue.use(Vuex);
export default new Vuex.Store({
state: {},
getters: {
getVacation: state => {
return state.vacation
},
guests: state => {
return state.vacation.guests
},
verifiedGuests: state => {
return state.vacation.guests.filter(guest => guest.verified)
},
emergencyContacts: state => {
return state.emergency_contacts
},
arrival: state => {
return state.vacation.check_in
},
departure: state => {
return state.vacation.check_out
},
countdown: state => {
let check_in = new Date(state.vacation.check_in);
let now = new Date();
if ((now - check_in) > 0) {
return 'This vacation started on ' + check_in;
}
let difference = check_in - now;
let day = 1000 * 60 * 60 * 24;
return Math.ceil(difference / day) + " days until your vacation";
}
},
mutations: {
[UPDATE_VACATION](state, payload) {
state.vacation = payload.vacation;
},
[ADD_GUEST](state, payload) {
state.vacation.guests.push(payload.guest);
},
[REMOVE_GUEST](state, payload) {
state.vacation.guests.filter(guest => {
debugger;
return guest.id != payload.guest.id
})
},
[UPDATE_GUEST](state, payload) {
state.vacation.guests.map(guest => {
// Refactor Object.assign to deep cloning of object
return guest.id === payload.guest.id ? Object.assign({}, guest, payload.guest) : guest;
})
},
[ADD_EMERGENCY](state, payload) {
state.vacation.emergency_contacts.push(payload.emergency_contact)
},
[REMOVE_EMERGENCY](state, payload) {
state.vacation.emergency_contacts.filter(contact => contact.id !== payload.emergency_contact.id)
},
[UPDATE_EMERGENCY](state, payload) {
state.vacation.emergency_contacts.map(contact => {
// Refactor not needed because emergency_contact is a shallow object.
return contact.id === payload.emergency_contact.id ? Object.assign({}, contact, payload.emergency_contact) : contact;
});
}
},
actions: {
getVacation({
commit
}) {
api.getVacation().then(vacation => commit(UPDATE_VACATION, vacation))
},
addGuest({
commit
}, guest) {
commit(ADD_GUEST, guest);
},
removeGuest({
commit
}, guest) {
commit(REMOVE_GUEST, guest);
},
updateGuest({
commit
}, guest) {
commit(UPDATE_GUEST, guest);
},
addEmergency({
commit
}, guest) {
commit(ADD_EMERGENCY, contact)
},
removeEmergency({
commit
}, contact) {
commit(REMOVE_EMERGENCY, contact)
},
updateEmergency({
commit
}, contact) {
commit(UPDATE_EMERGENCY, contact)
},
updateServer(store, payload) {
return api.saveVacation(payload)
}
}
});
Just so the solution is clear to others:
I wasn't setting my initial state quite properly in the store itself. I was pulling in the data, and updating the store correctly, but the store needed to be initialized like this:
export default new Vuex.Store({
state: {
vacation: {} //I added this, and then justed updated this object on create of the root Vue Instance
},
});
I think you're doing everything right. Maybe you're just not creating the getters correctly (can't see any definition in your code). Or your setting the initial state not correctly (also not visible in your snippet).
I would use mapState to have the state properties available in components.
In the demo simply add users to the array in mapState method parameter and the users data will be available at the component. (I've just added the getter users to show how this is working. That's not needed if you're using mapState.)
Please have a look at the demo below or this fiddle.
const api =
'https://jsonplaceholder.typicode.com/users'
const UPDATE_USERS = 'UPDATE_USERS'
const SET_LOADING = 'SET_LOADING'
const store = new Vuex.Store({
state: {
users: {},
loading: false
},
mutations: {
[UPDATE_USERS](state, users) {
console.log('mutate users', users)
state.users = users;
console.log(state)
}, [SET_LOADING](state, loading) {
state.loading = loading;
}
},
getters: {
users(state) {
return state.users
}
},
actions: {
getUsers({commit}) {
commit(SET_LOADING, true);
return fetchJsonp(api)
.then((users) => users.json())
.then((usersParsed) => {
commit(UPDATE_USERS, usersParsed)
commit(SET_LOADING, false)
})
}
}
})
const mapState = Vuex.mapState;
const Users = {
template: '<div><ul><li v-for="user in users">{{user.name}}</li></ul></div>',
computed: mapState(['users'])
}
new Vue({
el: '#app',
store: store,
computed: {
...mapState(['loading']),
//...mapState(['users']),
/*users () { // same as mapState
return this.$store.state.users;
}*/
users() { // also possible with mapGetters(['users'])
return this.$store.getters.users
}
},
created() {
this.$store.dispatch('getUsers')
},
components: {
Users
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch-jsonp/1.0.5/fetch-jsonp.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/2.1.1/vuex.min.js"></script>
<div id="app">
<div v-if="loading">loading...</div>
<users></users>
<pre v-if="!loading">{{users}}</pre>
</div>
You can create a function that returns the initial state, and use it into your Vuex instance, like this:
function initialStateFromLocalStorage() {
...
const empty = {
status: '',
token: '',
user: null
}
return empty;
}
export default new Vuex.Store({
state: initialStateFromLocalStorage,
...
As soon as you return an object for the state, you can do whatever you want inside that function, right?