Search code examples
javascriptvue-componentvuejs3dropdown

Unable to access sub-array in Vue 3 prop object item


In a Vue3/InertiaJS project, I am attempting to do a simple dynamic select list population, but I'm having issues reading the props data to populate the dropdown.

<script setup>
    import {Head, Link, useForm} from '@inertiajs/inertia-vue3'
    import SelectInput from '@/Components/ping/SelectInput.vue'
    import {ref} from 'vue';

      const props = defineProps({
    areas: [
      {
        id:1,
        name:"Club",
        approvers: [
          {
            id:12,
            first_name: "Joe",
            last_name: "Blow"
          }
        ]
      },
      {
        id:2,
        name:"Adult Small Bore",
        approvers: [
          {
            id:34,
            first_name: "Sam",
            last_name: "Snead"
          }
        ]
      },
      {
        id:3,
        name:"Appleseed",
        approvers: [
          {
            id:56,
            first_name: "Johhny",
            last_name: "Appleseed"
          }
        ]
      },
      {
        id:4,
        name:"Archery",
        approvers: [
          {
            id:56,
            first_name: "Jim",
            last_name: "Beam"
          },
          {
            id:56,
            first_name: "Jack",
            last_name: "Daniels"
          }
        ]
      },
      {
        id:5,
        name:"Armed Women of America",
        approvers: [
          {
            id: 99,
            first_name: "Amelia",
            last_name: "Earhart"
          }
        ]
      }
    ]  
  })

    const form = useForm({
        date: '',
        hours: '',
        area: '',
        reason: '',
        comments: '',
        approver: ''
    });

    let selectApprovers = ref([]);

    function updateApprovers(event) {
        const area = props.areas.filter(area => area.id === parseInt(event.target.value));
        console.log({area});
        selectApprovers.value = area.approvers.map(function (item) {
            return {
                id: item.id,
                name: item.first_name + ' ' + item.last_name
            }
        });

       selectApprovers.value = approvers;
    }

    function store() {
        form.post(`/admin/work-hours`)
    }
</script>

<template>
  <div>
    <form @submit.prevent="store">
    
                    <select-input v-model="form.area" :error="form.errors.area"
                                class="pb-8 pr-6 w-full lg:w-1/2" label="Area" @change="updateApprovers">
                        <option :value="null" />
                        <option v-for="area in props.areas" :key="area.id" :value="area.id">{{ area.name }}</option>
                    </select-input>
                    <select-input v-model="form.approver" :error="form.errors.approver"
                                class="pb-8 pr-6 w-full lg:w-1/2" label="Approver">
                        <option :value="null" />
                        <option v-for="approver in selectApprovers" :key="approver.id" :value="approver.id">{{ approver.name }}</option>
                    </select-input>
    </form>
  </div>
</template>

where the <select-list> input is just a wrapper around a standard <select> element.

The props in the real app are coming from Laravel via InertiaJS. The issue I am having is in updateApprovers(). This line works fine:

const area = props.areas.filter(area => area.id === parseInt(event.target.value));

as seen in the console.log({area}). However, it errors out at the next line when it tries to read area.approvers, and I get the error

Uncaught TypeError: can't access property "map", area.approvers is undefined

I've tried using value

 selectApprovers.value = area.value.approvers.map(function (item) {

but I get the same error. Visual inspection of the props in Vue dev tools shows that the approvers array is there; what do I need to change to be able to access it?

I've tried setting up an sfc playground, but for some reason it's having issues reading the v-for loop.


Solution

  • Array.filter always returns another array, so this line:

    const area = props.areas.filter(area => area.id === parseInt(event.target.value));
    

    results in area being an array of objects. Those individual objects may each have an inner approvers array, but the area array itself has no approvers property, which is why area.approvers.map() throws an error.

    You could do area[0].approvers.map(), but I think a more appropriate Array function to use in the first place would be Array.find to return the first matching object which you could then map.

    function updateApprovers(event) {
      const area = props.value.areas.find(
        area => area.id === parseInt(event.target.value)
      );
      selectApprovers.value = area.approvers.map(function (item) {
        return {
          id: item.id,
          name: item.first_name + ' ' + item.last_name
        };
      });
    }