javascriptvue.js

How can I loop through API resource data but exclude certain properties based on the key when displaying on the front end Vue template?


I'm working on creating a table component using Vuejs. The app uses laravel as the backend along with inertiajs. I have a resource that is returned for displaying a resource. In the resource I am trying to add some data that I don't want displayed, these will be links to edit and delete the resource. I also may have to add an ID for having a checkbox on each row of the table as well.

My question is how can I exclude the links and meta from being displayed in the loop of data but then show them as a checkbox or link? It's been a long day I hope this is clear.

PermissionResource

public function toArray($request)
{
    return [
        'name' => $this->name,
        'links' => [
            'edit' => action([PermissionController::class, 'edit'], $this),
            'delete' => action([PermissionController::class, 'destroy'], $this),
        ],
        'meta' => [
            'id' => $this->id
        ]
    ];
}

Table.vue

<script setup>
import { computed, ref } from 'vue';

const props = defineProps({
    data: {
        type: Object,
        required: true
    }
});

const hasActionLinks = ref(props.data.filter((item) => item.hasOwnProperty('links')));
const hasRowSelector = ref(props.data.filter((item) => item.hasOwnProperty('meta')));

</script>

<template>
    <div>
        <table>
            <thead>
                <tr>
                    <th v-if="hasRowSelector" scope="col">
                        <input id="selectAll" type="checkbox" v-model="selectAll" />
                    </th>

                    <th v-for="(heading, index) in Object.keys(data[0]).map((heading) => heading.replace(/_/g, ' '))" v-bind:key="index" scope="col">
                        {{ heading }}
                    </th>

                    <th v-if="hasActionLinks" scope="col">
                        Actions
                    </th>
                </tr>
            </thead>

            <tbody>
                <tr v-for="(row, index) in Object.values(data)" :key="index">

                    <td v-if="hasRowSelector">
                        <input type="checkbox" v-model="selected" :value="row.meta.id" />
                    </td>

                    <td v-for="(value, myindex) in Object.values(row)" v-bind:key="myindex">
            {{ value }}
                    </td>

                    <td v-if="hasActionLinks">
                        // EDIT AND DELETE LINKS HERE
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
</template>

This is the current result enter image description here


Solution

  • You could change your v-for="(value, myindex) in Object.values(row)" to ignore the property "links" and "meta". This could be done using either a function, or a computed value.

    <script>
    const unwantedColumns = ref(['links', 'meta']);
    
    const getFilterableProps = (row) => {
      return Object.values(row).filter(col => !unwantedColumns.value.includes(col));
    }
    </script>
    

    And then use this function in your template

                <tbody>
                    <tr v-for="(row, index) in Object.values(data)" :key="index">
    
                        <! -- ... -->
    
                        <td v-for="(value, myindex) in getFilterableProps(row)" v-bind:key="myindex">
                            <! -- ... -->
                        </td>
    
                         <! -- ... -->
                    </tr>
                </tbody>
    

    A more efficient way to do this would be to declare a "columns" array that contains all the columns you want to show and iterate through that, rather than attempting to dynamically defines the columns.

    <script>
    const columns = ref([
      'property1',
      'property2',
      ...
    ])
    </script>
    
    <template>
      <!-- ... -->
       <td v-for="(column, myindex) in columns" v-bind:key="myindex">
                {{ row[column] }}
       </td>
       <!-- ... -->
    </template>
    

    Anyways, I hope this gave you some ideas on how to fix the issue. Please note that the provided code is UNTESTED and should be used as reference only.