0
I am developing a front end application given a back end with some API.

The application has two similar views. Both views make use of a big table that's also used as a form . For example, in the table there are some values which are inputs or check boxes, or even buttons. That table also comes with a nice search in table functionality.


So I thought I would just make a new component and share it between the views. But since then I have been losing my mind over how I would make a clean design that's not hard to use .


What I have thought:

The component would take the objects and the attributes of the objects we want it to display as input (via props) and that's in common between of all my thoughts. Then to display the check boxes/inputs/buttons etc. it could do one of the following:

  1. It would use dynamic slots (e.g extraRow1, extraRow2, extraRow3 ...) to insert templates of HTML containing every HTML column in every row.
    For example:
    component.vue
    <template>
        <tr v-for=...>
            <td v-for=... >
                attributes of objects here, NON HTML
            </td>
            <slot v-for= ... :name=`extraRow${i}`>
        </tr>
    </template>
    

    parent.vue
    <template>
       <component objects=... attributesWanted=...>
           <template v-for=... #[`extraRow${i}`]>
                <!-- First Column -->
                <td> <input type='checkbox'> </input> </td>
                 ...            
           </template>
       </component>
    </template>
    

    Feels really hacky/dirty though, I feel shame writing that pseudo-code.

  2. Could pass the raw HTMLs in an array and go through it in component using v-for and v-html directives.
Of course, in both cases, if a button or submit input is activated the component could either active the parent method directly (even more coupling) or fire an event.

The reason I am here is that I don't feel proud for any of the solutions, because as you can notice are really poor.
Do you have any better solutions, do you think I shouldn't even use a component?

  • 1
    I don't see a concrete argument against the more straightforward approach of keeping the HTML in the actual web app where it belongs. Sure, there are some exceptions to this (e.g. rich text), but I see no relevant exception here. While I'm no Vue expert by any means, the trivial nature of sharing a component across multiple views makes me suspect that you're not using Vue the way it is designed to be used, and the answer to your question is better given via a Vue tutorial on how to create and integrate subcomponents. – Flater May 31 '22 at 07:53

1 Answers1

0

After some time searching, I finally found myself an answer. The answer was inspired by Vue Bootstrap 's tables.

For each row, in each column the table should use a slot named "column($columnName)" providing the traversal object to parent using scoped slots, and have fall back the objects[columnName] value.

Code for parent and component is demonstrated below:

MyTable.vue

<script>
    import { defineComponent } from "vue";

    export default defineComponent({
        props: ["objects", "fields"],
        data() {
            return {
                
            }
        }
    });
</script>

<template>
    <table border="1">
        <tr>
            <td v-for="field of fields">
                {{ field }}
            </td>
        </tr>
        <tr v-for="object of objects">
            <td v-for="field of fields">
                <slot :name="`column(${field})`" :object="object">
                    {{ object[field] }}
                </slot>
            </td>
        </tr>
    </table>
</template>

Parent.vue

<script>
    import { defineComponent } from "vue";
    import MyTable from "./components/MyTable.vue";

    export default defineComponent({
    data() {
        return {
            objects: [
                {
                    firstName: "Bob",
                    lastName: "Marley",
                    email: "bobmarley@gmail.com"
                },
                {
                    firstName: "John",
                    lastName: "Doe",
                    email: "jonedoe@hotmail.com"
                }
            ],
            fields: ["firstName", "lastName", "email", "deleteButtons"]
        };
    },
    components: { MyTable }
});
</script>

<template>
    <my-table :objects="objects" :fields="fields"> 
        <template #column(deleteButtons)="{object}">
            <button>
                Delete {{ object.firstName }}
            </button>
        </template>
    </my-table>
</template>

Result:

The result of the code