0

Right now I need some way to make all cells on my table editable. I know I'm able to do it like in this example. But it's just one cell per row, I'm wondering how I can achieve this for all cells on in a single row with its own v-model. I've been searching for examples but couldn't find any. Does anyone know how can I do it?

I'm able to edit all of them but when I click it, it opens the edition mode for all cells when in fact I want to open just the one I clicked.

This is how my code looks like now:

<template>
     <a-table bordered :data-source="dataSource" :columns="columns" :pagination="false">
        <template #title>
            <div class="formLayoutCrud">
                <p>{{this.title}}</p>
                <input-multiple-button :name="'buttonOptions'" :value="'percentage'" :options="this.buttonOptions"> </input-multiple-button>
            </div>
        </template>
        <template v-for="col in this.editableCells" #[col]="{ text, record }" :key="col">
            <div class="editable-cell">
                <div v-if="editableData[record.key]" class="editable-cell-input-wrapper">
                    <a-input v-model:value="editableData[record.key][col]" @pressEnter="save(record.key)" />
                    <check-outlined class="editable-cell-icon-check" @click="save(record.key)" />
                </div>
                <div v-else class="editable-cell-text-wrapper">
                    {{ text || ' ' }}
                    <edit-outlined class="editable-cell-icon" @click="edit(record.key, col)" />
                </div>
            </div>
        </template>
    </a-table>
</template>
<script>
import { reactive, ref } from 'vue';
import { CheckOutlined, EditOutlined } from '@ant-design/icons-vue';

import InputMultipleButton from '@/components/crudForm/InputMultipleButton.vue';

export default {
    name: 'TableEditable',
    props: {
        title: String,
        buttonOptions: Array,
        editableCells: Array,
        dataSrc: Array
    },
    components: {
        CheckOutlined,
        EditOutlined,
        InputMultipleButton
    },
    setup() {
        const columns = [
            {
                title: 'Mon',
                dataIndex: 'monday',
                slots: {
                    customRender: 'monday',
                },
            },
            {
                title: 'Tue',
                dataIndex: 'tuesday',
                slots: {
                    customRender: 'tuesday',
                },
            },
            {
                title: 'Wed',
                dataIndex: 'wednesday',
                slots: {
                    customRender: 'wednesday',
                },
            },
            {
                title: 'Thr',
                dataIndex: 'thursday',
                slots: {
                    customRender: 'thursday',
                },
            },
            {
                title: 'Fri',
                dataIndex: 'friday',
                slots: {
                    customRender: 'friday',
                },
            },
            {
                title: 'Sat',
                dataIndex: 'saturday',
                slots: {
                    customRender: 'saturday',
                },
            },
            {
                title: 'Sun',
                dataIndex: 'sunday',
                slots: {
                    customRender: 'sunday',
                },
            },
        ];
        const dataSource = ref([
            {
                key: '0',
                monday: '0',
                tuesday: '0',
                wednesday: '0',
                thursday: '0',
                friday: '0',
                saturday: '0',
                sunday: '0'

            }
            ]);
        const editableData = reactive({});

        const edit = (key, teste) => {
            console.log(teste)
            editableData[key] = JSON.parse(JSON.stringify(dataSource.value.filter(item => key === item.key)[0]));
        };

        const save = key => {
            console.log(key)
            Object.assign(dataSource.value.filter(item => key === item.key)[0], editableData[key]);
            delete editableData[key];
        };
        return {
            columns,
            dataSource,
            editableData,
            edit,
            save
        };
  },
}
</script>

Thanks in advance!

1 Answers1

0

TL;DR:

use composite key, like editableData[row + '|' + column] to target row and column for editing.

The example (that I assume you intended to link) has a couple shortcomings.

If you'll pardon the "pedantics", I'll explain what the example does first. I've kept the important bits here:

// in template:  @click="edit(record.key)"

// in setup:
const editableData = reactive({});

function edit(key){
  editableData[key] = JSON.parse(
    JSON.stringify(dataSource.value.filter((item) => key === item.key)[0])
  );
}

editableData is a reactive object that will store two parameters the key and the copy of the entire object. for example assigning editableData[2] = {...} will allow the save method to replace the 3rd item (key) to the altered copy. One issue with that is that all the fields in the same row are tied together. So if you edit two, but only save one, they will both be updated. The second issue is that there is no way to target which specific value in the object you are making editable. Additionally, relying on the copy of an entire object, could cause some issues if the underlying data is mutable through some other path.

There are couple different ways to solve for this issue, but I'll just share, what I believe to be, the simplest, which is to use a composite key.

instead of editableData[key] = {a copy of row object} you can use editableData[rowKey + "|"+ colKey] = 12 (12 being example of the value when user hits edit)

it would look something like this:

template:

<template
  v-for="col in editableCells"
  #[col]="{ column, text, record }"
  :key="col"
>
  <div class="editable-cell">
    <div
      v-if="editableData[record.key + '|' + column.key]"
      class="editable-cell-input-wrapper"
    >
      <a-input
        v-model:value="editableData[record.key + '|' + column.key]"
        @pressEnter="save(record.key, column.key)"
      />
      <check-outlined
        class="editable-cell-icon-check"
        @click="save(record.key, column.key)"
      />
    </div>
    <div v-else class="editable-cell-text-wrapper">
      {{ text || ' ' }}
      <edit-outlined
        class="editable-cell-icon"
        @click="edit(record.key, column.key)"
      />
    </div>
  </div>
</template>

script:

const edit = (row, column) => {
  editableData[row + '|' + column] = dataSource.value.filter(
    (item) => row === item.key
  )[0][column];
};

const save = (row, column) => {
  dataSource.value[row][column] = editableData[row + '|' + column];
  delete editableData[row + '|' + column];
};

I'm using the pipe character as a separation character to concatenate the two strings, you can omit that and just use row + column which should resolve to a string and not cause any issues. This can be handy though if you have a potential for conflicting keys (such as both being indexes)

Daniel
  • 34,125
  • 17
  • 102
  • 150
  • Hello Daniel! Thank you so much for the answer, it solved my problem and I learned a lot with it. – Gabriella Tavares Feb 08 '22 at 14:00
  • Now I'm having a different problem. Then I try to delete the content in the input and the length of the content is 1, the input closes and I have no idea why. I'm having some trouble trying to figure it out why this is happening. – Gabriella Tavares Feb 08 '22 at 18:48
  • can you create a separate question with the relevant details? – Daniel Feb 08 '22 at 20:46
  • yeah, sure! here it is https://stackoverflow.com/questions/71047195/input-closes-when-i-try-to-delete-the-string-and-the-string-has-the-length-1-on – Gabriella Tavares Feb 09 '22 at 09:43