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 >
<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 >
<div v-if="editableData[record.key]" >
<a-input v-model:value="editableData[record.key][col]" @pressEnter="save(record.key)" />
<check-outlined @click="save(record.key)" />
</div>
<div v-else >
{{ text || ' ' }}
<edit-outlined @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!
CodePudding user response:
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 >
<div
v-if="editableData[record.key '|' column.key]"
>
<a-input
v-model:value="editableData[record.key '|' column.key]"
@pressEnter="save(record.key, column.key)"
/>
<check-outlined
@click="save(record.key, column.key)"
/>
</div>
<div v-else >
{{ text || ' ' }}
<edit-outlined
@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)