PrimeVue - DataTable duplicate key error on reorderableColumns - vue.js

I have the below datatable with a strange behavioir:
<DataTable
:scrollable="true"
:value="shipments"
:totalRecords="shipments.length"
:reorderableColumns="true"
:alwaysShowPaginator="false"
:paginator="true"
:rows="10"
:resizableColumns="true"
columnResizeMode="fit"
sortMode="multiple"
:stripedRows="true"
removableSort
dataKey="reference"
responsiveLayout="scroll">
<template #empty> No records found </template>
<Column field="reference" header="Shipment Number" :sortable="true" frozen />
<Column header="Shipper" style="text-transform: capitalize">
<template #body="slotProps">
{{ slotProps.data.shipper.name.toLocaleLowerCase() }}
</template>
</Column>
</DataTable>
If I try to reorder (dragging) the columns, I get the below error. Every time I try to reorder, the reference column is added to the table.
[Vue warn]: Duplicate keys found during update: "reference" Make sure keys are unique.
If I remove this part of the Shipper column:
<template #body="slotProps">
{{ slotProps.data.shipper.name.toLocaleLowerCase() }}
</template>
And just reference the shipper name using field="shipper.name" it works fine without any errors.
What am I doing wrong?

Reading through the documentation I can see that I missed an important part when using the reorderColumns functionality.
I need to set a columnKey if the specific column doesn't have a field defined as per the docs:
If the column has no field, use columnKey instead as it is mandatory for columns to have unique keys when reordering is enabled.
I added this, and it works now:
<Column header="Shipper" columnKey="shipper" style="text-transform: capitalize">
<template #body="slotProps">
{{ slotProps.data.shipper.name.toLocaleLowerCase() }}
</template>
</Column>

Related

PrimeVue Datatable - Default field value

I have a simple datatable with PrimeVue, where I can add columns like below:
<Column field="destination" header="Destination"></Column>
This will render a column with the field destination for that specific row. However, in some cases, the destination will be null. Is there an easy way to define a default value - or do I have to use templating like below?
<Column field="destination" header="Destination" :sortable="true">
<template #body="slotProps">
{{ slotProps.data.destination ?? 'N/A' }}
</template>
</Column>
The reason why I don't want to use the template and slotProps is simply because I have a very large datatable with +50 columns, and this just looks messy.

Use fallback content of slot only if condition is met

I would like to know if there is a way to do what I'm trying to describe below:
Let's suppose we have a component with a slot, and a fallback content has been defined.
When using this component elsewhere, I would like to have the following behavior:
<TheComponent>
<template v-slot:name="{ data }">
<fallback v-if="condition(data)"/>
</template>
</TheComponent>
I suppose the fallback tag (or similar) does not exists (at least, I didn't find it...). So I suppose I'm thinking the wrong way, but I can't find a solution to my problem.
The thing is that I can't alter the TheComponent as it is provided by an external library, and I don't want to re-create the content.
If it can help, in fact, I'm trying to hide the expand button to prevent to expand a row in a Vuetify data-table, depending on if the row has something to show or not in it's expanded part. So I would like to write something that behave like:
<v-data-table :items="items" show-expand ...>
<template v-slot:item.data-table-expand="{ item }">
<!-- Here I want the default behavior only if my item respects some condition -->
<fallback v-if="condition(item)"/>
<!-- Otherwise, I don't want to display the button that expands the row -->
</template>
</v-data-table>
Thank you in advance for your help.
After quite a lot of "googling" I don't think its possible. IMHO your best bet is to replicate the default slot content Vuetify generates and put it under your own condition (v-if="item.description" in my example):
<v-data-table :headers="headers" :items="people" show-expand>
<template v-slot:expanded-item="{ item, headers }">
<td :colspan="headers.length">{{ item.description }}</td>
</template>
<template v-slot:item.data-table-expand="{ item, isExpanded, expand }">
<v-icon
v-if="item.description"
:class="['v-data-table__expand-icon', { 'v-data-table__expand-icon--active': isExpanded }]"
#click.stop="expand(!isExpanded)"
>$expand</v-icon>
</template>
</v-data-table>
I understand this solution is brittle and can break anytime Vuetify change something but i don't think there is better solution now...
Example

Sortable not working in el-table-column with checkbox

When i try to sort by using either first or second columns, the rows are sorted except for the checkbox in the last column. Is the last column disjoint based on my code?
<el-table :data="items" stripe style="width: 50%">
<el-table-column prop="email" label="email" sortable></el-table-column>
<el-table-column prop="username" label="Username" sortable></el-table-column>
<el-table-column label="Enable">
<template slot-scope="scope">
<el-checkbox :checked="scope.row.isSelected" #change="toggleEnable(scope.row)" />
</template>
</el-table-column>
</el-table>
So basically the DOM isn't changed so Vue doesn't recognize the change. To give Vue a hint so that it can track each node’s identity, and thus reuse and reorder existing elements, you need to provide a unique key attribute for each item. In case of element ui table you should fill the row-key attribute. Which is listed in table-atributes
Adding the row-key will solve your challenge
An alternative way to make it work is replacing the :checked by v-model. but still it's good to provide the row-key

bootstrap-vue toggle expand table row

This seems to remain unanswered so here is another attempt at a solution.
Currently in bootstrap-vue, I am rendering a b-table. I would like to improve this by having the ability to select a row and collapse/expand an extra div/row/etc to show further information.
In the below snippet you will see what I am trying. The problem is that I can't seem to get the expanded data to span the number of columns in the table. I have tried adding <tr><td colspan="6"></td></tr> but it doesn't seem to span like I would expect. Any workarounds for this? Thanks.
<b-table
:items="case.cases"
:fields="tableFields"
head-variant="dark">
<template
slot="meta.status"
slot-scope="data">
<b-badge
v-b-toggle.collapse1
:variant="foobar"
tag="h6">
{{ data.value }}
</b-badge>
</template>
<template
slot="#id"
slot-scope="data">
<span
v-b-toggle.collapse1>
{{ data.value }}
</span>
<b-collapse id="collapse1">
Collapse contents Here
</b-collapse>
</template>
</b-table>`
Sounds like you could use the Row Details slot:
If you would optionally like to display additional record information (such as columns not specified in the fields definition array), you can use the scoped slot row-details
<b-table :items="case.cases" :fields="tableFields" head-variant="dark">
<template slot="meta.status" slot-scope="data">
<b-button #click="data.toggleDetails">
{{ data.value }}
</b-button>
</template>
<template slot="row-details" slot-scope="data">
<b-button #click="data.toggleDetails">
{{ data.detailsShowing ? 'Hide' : 'Show'}} Details }}
</b-button>
<div>
Details for row go here.
data.item contains the row's (item) record data
{{ data.item }}
</div>
</template>
</b-table>
There is a good example in the docs at https://bootstrap-vue.js.org/docs/components/table#row-details-support
I (think) I had the same issue, and I came up with a solution which leverages the filtering functionality of the bootstrap-vue <b-table> to achieve the effect of expanding and collapsing rows.
There's a minimal example in a JSFiddle here:
https://jsfiddle.net/adlaws/mk4128dg/
Basically you provide a tree structure for the table like this:
[
{
columnA: 'John', columnB:'Smith', columnC:'75',
children:
[
{ columnA: 'Mary', columnB:'Symes', columnC:'46' },
{ columnA: 'Stan', columnB:'Jones', columnC:'42' },
{ columnA: 'Pat', columnB:'Black', columnC:'38' },
]
}
]
The tree is then "flattened" out to rows which can be displayed in a table by the _flattenTreeStructure() method. During this process, the rows are also annotated with some extra properties to uniquely identify the row, store the depth of the row (used for indentation), the parent row of the row (if any) and whether or not the row is currently expanded.
Once this is done, the flattened structure can be handed to the <b-table> as it is just an array of rows - this is done via the computed property flattenedTree.
The main work now is done by the _filterFunction() method which provides custom filtering on the table. It works off the state of the expandedRowIndices property of the filterObj data item.
As the expand/collapse buttons are clicked, the row index (as populated during the flattening process) is inserted as a key into expandedRowIndices with a true or false indicating its current expanded state.
The _filterFunction() uses this to "filter out" rows which are not expanded, which results in the effect of expanding/collapsing a tree in the table.
OK, so it works (yay!), but...
it's not as flexible as the base <b-table>; if you want to show different columns of data, you'll need to do some work and to re-do the <template slot="???"> sections for the columns as required.
if you want to actually use filtering to filter the content (with a text search, for example) you'll need to extend the custom filter function to take this into account as well
sorting the data is not something I had to do for my use case, and I'm not sure how it would work in the context of a tree structure anyway - maintaining the tree's parent/child relationships while changing the order of the rows around would be... fun, and I suspect this would be a nice challenge to implement for someone who is not as time poor as me. ;)
Anyway, I hope this is of use to someone. I'm reasonably new to Vue.js, so there may be a better way to approach this, but it's done the job I needed to get done.

how to access header index in vue-good-table

How do you access these header's index in a grouped table?
table_screenshot
You can get column name using props.column. props.column will give you full column object so you can get index from it. props.column.field will give you column name title.
You can check index with props.column.originalIndex
The original row object can be accessed via props.row
The currently displayed table row index can be accessed via props.index .
The original row index can be accessed via props.row.originalIndex.
You can then access the original row object by using rows[props.row.originalIndex].
The column object can be accessed via props.column
You can access the formatted row data (for example - formatted date) via props.formattedRow
For example :
<vue-good-table
:columns="columns"
:rows="rows">
<template slot="table-row" slot-scope="props">
<span v-if="props.column.field == 'age'">
<span style="font-weight: bold; color: blue;">{{props.row.age}}</span>
</span>
<span v-else>
{{props.formattedRow[props.column.field]}}
</span>
</template>
</vue-good-table>
Source
i just found it out, the answer was just in the picture
{{props.row.vgt_id}}
try this:
{{props.row.originalIndex}}