Caio Kawasaki
Caio Kawasaki

Reputation: 2950

Vuex - Modify state objects only on getters

I have a lot of state modules with relations, here are two of my main states:

channels.js

const state = {
    channels: [
        {
            id: 1,
            name: 'E-mail'
        },
        {
            id: 2,
            name: 'SMS'
        }
    ]
}

const getters = {
    channel(state) {
        return (id) => {
            return _.find(state.channels, (channel) => {
                return channel.id == id
            })
        }
    },
    channels(state) {
        return state.channels
    }
}

wallets.js

const state = {
    wallets: [
        {
            id: 1,
            name: "Wallet A",
            channel_id: 1
        },
        {
            id: 2,
            name: "Wallet B",
            channel_id: 2
        }
    ]
}

const getters = {
    wallet(state) {
        return (id) => {
            return _.find(state.wallets, (wallet) => {
                return wallet.id == id
            })
        }
    },
    wallets(state) {
        return state.wallets
    }
}

When I call the getter wallets/wallets, instead of return:

[
    {
        id: 1,
        name: "Wallet A",
        channel_id: 1
    },
    {
        id: 2,
        name: "Wallet B",
        channel_id: 2
    }
]

Is there any way to return like this?

[
    {
        id: 1,
        name: "Wallet A",
        channel: {
            id: 1,
            name: 'E-mail'
        }
    },
    {
        id: 2,
        name: "Wallet B",
        channel: {
            id: 2,
            name: 'SMS'
        }
    }
]

Edit


Based on 8bit answer, I tried the following code, but no success:

import {http} from "../../support/http";
import axios from "axios";

const state = {
    actions: [],
    bots: [],
    conditions: []
}

const getters = {
    action(state) {
        return (id) => {
            return _.find(state.actions, (action) => {
                return action.id == id
            })
        }
    },
    actions(state) {
        return state.actions
    },
    bot(state) {
        return (id) => {
            return _.find(state.bots, (bot) => {
                return bot.id == id
            })
        }
    },
    bots(state, getters, rootState, rootGetters) {
        return state.bots.map((bot) => {
            let channels = bot.channels.map((item) => {
                let channel = rootGetters["channels/channel"](item.channel_id)
                let wallet = rootGetters["wallets/wallet"](item.wallet_id)

                return {
                    ...item,
                    channel,
                    wallet
                }
            })

            return {
                ...bot,
                channels: channels
            }
        })
    },
    condition(state) {
        return (id) => {
            return _.find(state.conditions, (condition) => {
                return condition.id == id
            })
        }
    },
    conditions(state) {
        return state.conditions
    },
    primaryConditions(state) {
        return _.filter(state.conditions, (condition) => {
            return condition.primary == true
        })
    },
    secondaryConditions(state) {
        return _.filter(state.conditions, (condition) => {
            return condition.primary == false
        })
    }
}

const actions = {
    fetchData({dispatch}) {
        function getActions() {
            return http.get('idr/actions')
        }

        function getBots() {
            return http.get('idr/bots')
        }

        function getConditions() {
            return http.get('idr/conditions')
        }

        return axios.all([
            getActions(),
            getBots(),
            getConditions()
        ]).then(axios.spread(function (actions, bots, conditions) {
            dispatch('setActions', actions.data.data)
            dispatch('setBots', bots.data.data)
            dispatch('setConditions', conditions.data.data)
        })).catch(error => console.error(error))
    },
    insertChannel({commit}, channel) {
        commit('INSERT_CHANNEL', channel)
    },
    setActions({commit}, actions) {
        commit('SET_ACTIONS', actions)
    },
    setBot({commit}, bot) {
        commit('SET_BOT', bot)
    },
    setBots({dispatch}, bots) {
        _.each(bots, (bot) => {
            dispatch('setBot', bot)
        })
    },
    updateBot({commit}, data) {
        commit('UPDATE_BOT', data)
    },
    deleteBot({commit}, bot) {
        commit('DELETE_BOT', bot)
    },
    setConditions({commit}, conditions) {
        commit('SET_CONDITIONS', conditions)
    }
}

const mutations = {
    INSERT_CHANNEL(state, data) {
        let index = _.findIndex(state.bots, {id: data.bot_id});

        state.bots[index].channels.push(data.channel)
    },
    SET_ACTIONS(state, actions) {
        state.actions = actions
    },
    SET_BOT(state, data) {
        let index = _.findIndex(state.bots, {'id': data.id})

        index > -1 ? state.bots[index] = data : state.bots.push(data)
    },
    UPDATE_BOT(state, data) {
        let index = _.findIndex(state.bots, {id: data.bot_id});

        state.bots[index].channels = data.channels
    },
    DELETE_BOT(state, bot) {
        let bot_index = _.findIndex(state.bots, {id: bot.id});

        state.bots.splice(bot_index, 1)
    },
    SET_CONDITIONS(state, conditions) {
        state.conditions = conditions
    }
}

export default {
    namespaced: true,
    state,
    getters,
    actions,
    mutations
}

The getter is returning the data correctly, but when I add an specific bot to a data property the new properties go away...

enter image description here

Bot is added to data this way:

<list-item v-for="bot in botsGetter" :key="bot.id" class="white-hover">
    <dropdown class="align-center">
            <template slot="menu">
                <li><a href="#" title="" @click.prevent="editBot(bot)">Editar</a></li>
            </template>
        </dropdown>
</list-item>

editBot(bot) {
    this.$bus.$emit('hide.dropdown')

    this.editBotModal = bot
}

Upvotes: 0

Views: 827

Answers (1)

8bit
8bit

Reputation: 597

You have access to rootGetters from any getter in any module, check the Vuex API here.

So you could write your wallets getter like this:

wallets(state, getters, rootState, rootGetters) {
  return state.wallets.map((wallet) => {
    const channel = rootGetters["channel/channel"](wallet.channel_id);

    return {
      ...wallet,
      channel
    }
  }
}

Or alternatively, if you keep your state normalized (which it seems you do), then you can use denormalize, but you might have to actually originally normalize the store using the same library too, and keep a set of schemas.

Upvotes: 2

Related Questions