0

I want to access data from a prop that has an array within an array, I have to type tons of v-for statements to access the data.

Currently I have to cycle through the spaces array (check if their id matches with the one I want) and use an v-if statement to output only the one I want, then I repeat that cycle for rooms (check ids again).

Is there a way to filter through arrays within arrays? Without these for/if statements? I've been reading into filters but haven't been able to figure it out for what I need.

Vue file:

               <v-flex hidden-sm-and-down sm4 lg4 class="sidebarSticky">
                    <v-layout v-for="space in spaces"
                              v-if="space.id === selectedSpace"
                              :key="space.id"
                    >
                        <!-- 1 room details -->
                        <v-flex v-for="room in space.rooms"
                                v-if="selectedRoom === room.id"
                                v-if="selectedRoom "
                                :key="room.id">
                            <v-card>
                                <v-card-text>

                                    <!-- room name -->
                                    <h2 class="sidebarRoomName"> {{ room.name }} </h2>
                                    <!-- description -->
                                    <p> {{ room.description }} </p>

                                </v-card-text>
                            </v-card>
                        </v-flex>
                    </v-layout>
                </v-flex>

My Data:

    new Vue({
        el: '#app',
        data: () => ({
            selectedSpace: 0;
            selectedRoom: 1,
            spaces: [
                {
                    id: 0,
                    name: 'House in Amsterdam',
                    rooms: [
                        {
                            id: 0,
                            name: 'My epic living room'
                        },
                        {
                            id: 1,
                            name: 'My epic room'
                        }
                    ]
                },
                {
                    id: 5,
                    name: 'House in Paris',
                    rooms: [
                        {
                            id: 0,
                            name: 'My epic bathroom'
                        }
                    ]
                }
            ]
        })
    })

The code above might seem very simple, but I'm working with a lot more data and it can be super difficult to work with.

Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
Otto
  • 663
  • 3
  • 17
  • 33
  • Possible duplicate of [How to filter multidimensional javascript array](https://stackoverflow.com/questions/9206914/how-to-filter-multidimensional-javascript-array) – Roberto Geuke Jun 29 '18 at 12:00

1 Answers1

3

Ah yep, don't do that with v-for. Create yourself some computed properties and use Array.filter(), then in your template, point the computed property.

computed: {
    selectedSpaceObj() {
        return this.spaces.filter(aSpace => aSpace.id === this.selectedSpace)
    },
    selectedRoomObj() {
        return this.selectedSpaceObj.rooms
            .filter(aRoom => aRoom.id === this.selectedRoom)                
    }
}

Do it all in javascript. It will lead to much more readable code, and you will likely reuse the computed property all over the place. For selectedRoomObj, for example, we can just reuse the computed selectedSpaceObj.

Next, for code readability, and to avoid long accessors, create yourself space and room components. Nested v-for loops is a sure sign that you need them.

The problems you encounter might lead you to rethink the structure of your data. Perhaps you want to store selected as a property directly on the room and space objects? Perhaps it makes more sense to have 1 root level rooms array, with a "foreign key" into spaces?

Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
bbsimonbb
  • 27,056
  • 15
  • 80
  • 110
  • I edited out the `me` variable as it's not necessary when using arrow functions. – Emile Bergeron Jun 29 '18 at 14:58
  • @EmileBergeron thanks for the extra help man! I'm having an issue with the `selectedRoomObj()`, it is receiving an error of: `vue.js:597 [Vue warn]: Error in render: "TypeError: Cannot read property 'filter' of undefined"`. Eventhough it has a value. This is how I'm using it: ` {{ room.id }} ` This works for the `selectedSpaceObj` but not for the room! – Otto Jul 02 '18 at 09:38
  • Ok you'll need to modify your computed `selectedRoomObj` so that it never returns `undefined`. If filter finds nothing, `return {};` To be safe, you should do the same thing for selectedSpace :-) – bbsimonbb Jul 02 '18 at 09:42
  • @EmileBergeron `selectedRoomObj() { if (this.selectedSpaceObj == null) { return {}; } else { this.selectedSpaceObj.rooms.filter(aRoom => aRoom.id === this.selectedRoom) } }` Is this correct? I'm still getting the same error:/ It still says it `Cannot read property 'filter' of undefined"` – Otto Jul 02 '18 at 10:15
  • Change your test. Try... `if (!this.selectedSpaceObj || !this.selectedSpaceObj.rooms || this.selectedSpaces.rooms.length === 0) { return {}; }` That will deal with `undefined` as well. – bbsimonbb Jul 02 '18 at 10:20
  • @bbsimonbb It should be be working but for some reason when the page loads, it runs `selectedRoomObj` twice, eventhough I'm only using it once on my page. Then I figured out that in my `mounted()` I set my default value for the `this.selectedRoom` and `this.selectedSpace`. Could this be affecting my outcome? If I change `mounted()` to `created()` it works. – Otto Jul 02 '18 at 10:41
  • You shouldn't care about how many times these things run, or when. They run whenever they need to. I can't see why mounted or created should make any difference, but I can't see your code. – bbsimonbb Jul 02 '18 at 12:52