dib258
dib258

Reputation: 715

How to manage nested arrays in Vuex Store and pass it trough component

In my Vuex Store, I have a planning state where it describe the planning of a week.

planning : [
        {
            name : 'monday',
            meetings : [
                {
                    name : 'morning',
                    value : ''
                }, {
                    name : 'noon',
                    value : ''
                }, {
                    name : 'evening',
                    value : ''
                }]
        },
        {
            name : 'tuesday',
            meetings : [
                {
                    name : 'morning',
                    value : ''
                }, {
                    name : 'noon',
                    value : ''
                }, {
                    name : 'evening',
                    value : ''
                }]
        },

        ...

    }
]

To display everything I have something like this

PlanningWeek.vue

<template>
    <div class="week columns is-desktop is-vcentered">
        <PlanningDay v-for="(day, index) in planning" :key="index" :day="day"></PlanningDay>
    </div>
</template>

<script>
    import PlanningDay from './PlanningDay';
    import { mapState } from 'vuex';

    export default {
        computed : mapState(['planning']),
        components : {
            PlanningDay
        },
    };
</script>

And for each day I have PlanningDay.vue which retrieve the day from his props and then render it but is it the Vuex kind of way. Since Normally we always have to get/set (commit) from the Vuex Store.

What would be the best way to render nested arrays in Vuex Store ?

I continue with the nested code so that you have everything to see

PlanningDay.vue

<template>
    <div class="day column">
        <p class="name has-text-centered" v-text="day.name"></p>

        <PlanningMeeting v-for="(meeting, index) in day.meetings" :key="index" :meeting="meeting"></PlanningMeeting>
    </div>
</template>

<script>
    import PlanningMeeting from './PlanningMeeting';

    export default {
        props : ['day'],
        components : {
            PlanningMeeting
        }
    };
</script>

And then finally the PlanningMeeting.vue

<template>
    <div class="meeting">
        <p class="has-text-centered" v-text="meeting.name"></p>
        <input type="text" v-model="meeting.value">      
    </div>
</template>

How could I be able to bind meeting.value which came from the props from PlanningDay which comme from PlanningWeek ?

Since modifications are made with commit to change the state this would look like something like this :

<script>
    export default {
        props : ['meeting'],
        computed : {
            meetingList : {
                get() {
                    return this.$store.state.planning[planningId].meetings[meetingId];
                },
                set(value) {
                    this.$store.commit('updateValue', ...planningId, meetingId, ... value);
                }
            }
        },
    };
</script>

So I would bind the v-model with meetingList computed properties or something like this but how to manage to retrieve the planningId and meetingId, I mean in the proper way. Since the point of Vuex is to not put event, and props everywhere and extract this data layer.

So am I doing something wrong ? and how is the best way to manage nested arrays in Vuex to pass data to nested component ?

Thanks in advance for you answer !

Upvotes: 3

Views: 2940

Answers (2)

Dhanesh Agrawal
Dhanesh Agrawal

Reputation: 323

The best thing to do is to avoid nested structures. This is an established pattern in other global state single state tree systems like redux as well. But no need for Redux here. Normalizing the data is a good idea, yes - and the patterns that are used in redux for this stuff are applicble to vuex as well.

Normalization bascially means that you turn this:

planning : [
    {
        name : 'monday',
        meetings : [
            {
                name : 'morning',
                value : ''
            }, {
                name : 'noon',
                value : ''
            }, {
                name : 'evening',
                value : ''
            }]
    },
    {
        name : 'tuesday',
        meetings : [
            {
                name : 'morning',
                value : ''
            }, {
                name : 'noon',
                value : ''
            }, {
                name : 'evening',
                value : ''
            }]
    },

    ...

}
]

into this :

planning: {
  name: [monday,tuesday],
  meetings: {
    monday: { id: 'monday', name: [1,2,3] },
    tuesday: { id: 'tuesday', name: [4,5,6] },
  }
},
name : {
   meetings : {
    1: {id: 1, text: 'morning' , value: ''},
    2: {id: 2, text: 'noon' , value: ''},
    3: {id: 3, text: 'evening' , value: ''},        
    4: {id: 4, text: 'morning' , value: ''},
    5: {id: 5, text: 'noon' , value: ''},
    5: {id: 6, text: 'evening' , value: ''},        
    }
}

And now you can implement easy getters and setters into Vuex Store.It seems like it’s more complicated, and in a way it is, a little. But the advantage is that for updates, you will not have to worry about any nesting-related issues when writing mutations.

Ex: if you need a sorted array of all planning, you write a getter:

planningArray (state) => {
  return state.planning.list.map { 
            name => state.planning.meetings[name]
  }
}

Using this you do not need to pass props. For every change you push the data into the Store and retrieve it into another component using "computed/methods"

Upvotes: 4

Jefry Dewangga
Jefry Dewangga

Reputation: 849

If I'm not mistaken, you're trying to save a value from an input into vuex state which is meetings.value object.

First of all, you need to bind your input into separate object. Kind this way:

data () {
  return {
    value: null
  }
}

then in your template section,

<input type="text" v-model="value">
<button @click="onValueSaved"></button>

and you need to declare a method to saved the value into vuex state,

methods: {
   onValueSaved () {
     this.$store.commit('SET_MEETING_VALUE', this.value)
   }
}

Upvotes: 0

Related Questions