Home > Software engineering >  How to load Vue components into :rows of a table
How to load Vue components into :rows of a table

Time:10-28

I have a table listing my todos. In the last column, I have a button to mark the todo as finished. Because I'd like to reuse this button in other tables and views, I've added it as a component that is being imported.

Furthermore, I'd like to use a table package such as vue3-table-lite so that I can have quick and easy access to 1) filtering, 2) pagination, and 3) sorting features.

These packages typically uses :rows, which is pretty straightforward if you only have text. However, I don't know how to provide Vue components, in this case <FinishedButton /> from the last <td> shown in the last code block below.

How can I import Vue components into :rows of a table? Alternatively achieve filtering, pagination, and sorting without having to use :rows at all.


Here's how the button component is constructed:

<template>
    <a v-show="todo.is_finishable" v-on:click="markFinished(todo.id)"
        >Finished</a>
</template>

<script>
    export default {
        name: 'FinishedButton',
        props: ['todo',],

        methods: {
            remove: function () {
                this.$emit("delete", this.todo);
            },

            markFinished: function (selected_todo_id) {
                this.$http.patch('/api/todo/'   selected_todo_id   "/", {
                        status: 'Done'
                    })
                    .then(response => {
                        console.log(response);
                        this.remove();
                    })
                    .catch(e => {
                        console.log(e);
                    });

            }

        },
    };
</script>

Here's how a simplified version of the table looks like (there's more columns in the actual table):

<table id="FOO" >
    <thead>
      <tr>
        <th scope="col">Name</th>
        <th scope="col">Action</th>
      </tr>
    </thead>

    <tbody>
      <tr v-for="todo in todos" :key="todo.id">
        <td>{{ todo.name }}</td>
        <td><FinishedButton :todo=todo /></td>
      </tr>
    </tbody>
  </table>

CodePudding user response:

While I'm not familiar with that particular library, its documentation states that if you want to use custom rendering of rows (columns?) you should use v-slot mode.

What is a v-slot? To get the basics, you should probably get familiar with Vue v-slot documentation first. It is an excellent resource to get started. But tldr, slots are a way of leaving "boxes" in your child components for "stuff" to be rendered into them from the parent component. Like props, but for template content.

Why are they useful? Well, if you're making a library that many people use, or if you reuse your components a lot throughout your app, you may find it useful to leave the "looks" to the parent component implementation. You can also have default looks set.

Within slots, there is a concept of scoped slots, that solves a problem where a parent component needs to access the data from the child component. Scoped slots are props for your slot outlets.

Now, back to the problem at hand. While it is not the same library, Vuetify has a table component as well, with slots that represent the whole row, or a particular column. Namely, item and item.column-name slots respectively.

An example of rendering a custom column would look something like this

<template v-slot:item.column="{ item }">
    <MyCustomComponent :data="item" />
</template>

But I leave a proper showcase of this to Vuetify documentation.

For your use case, you might need to take over the rendering of the entire row if you want to have a custom rendered column.


Resources:

CodePudding user response:

No problem! This has to do with the way Vue parses the DOM. In order to avoid confusion with native HTML vs custom components. You need to tell Vue that your dynamic component may be used in this native table using the 'is' special attribute.

<td is="vue:finished-button" :todo="todo"></td>

Note that you must use kebab-case as shown because HTML tag names are case-insensitive. You do not need to rename your component as Vue automatically handles this behind the scenes.

  • Related