primevue
primevue copied to clipboard
DataTable: Wrapper for custom columns not working when using slots
Describe the bug
With the latest few releases we got a way to use costum wrappers for Column components for DataTables. This seems to work if you use the custom wrapper directly as child component, but not if you use slots.
See my stackblitz link for a comparison:
- The upper table only displays the normal columns, but not the two wrappers, because I use them as a slot to be more dynamic
- The lower table displays all of them
I've tried to debug this a bit and it seems like this line is the issue: https://github.com/primefaces/primevue/blame/9c162ae9e67d2f7df825613db58773b49b1100f7/components/lib/utils/HelperSet.js#L41 The components are passed towards the recursive function, even though it should be the helper. If I change that locally to pass the helpers instead the components, it does work, but I haven't tested further if that breaks anything else.
Reproducer
https://stackblitz.com/edit/primevue-datable-slots-customcolumnwrapper-issue?file=src%2FApp.vue
PrimeVue version
3.47.2
Vue version
3.x
Language
TypeScript
Build / Runtime
Vite
Browser(s)
all
Steps to reproduce the behavior
- Simply go to the provided stackblitz
- Upper table does not display custom columns/wrappers
- Lower table does
Expected behavior
Both tables should display all columns
Thanks a lot for your report! I set a milestone for it. We'll check it before the milestone is released.
Why do I need to set the key
for custom columns but not for normal columns:
<DataTable :value="rows">
<Column field="name" header="Name"></Column>
<CustomColumn key="phone" field="phone" header="Phone"></CustomColumn>
<Column field="email" header="Email"></Column>
</DataTable>
If I remove the key="phone", the whole column disappears. I have been wasting so much time trying to figure out what is wrong with my code because of that. I'm leaving this here in case someone is having the same problem as I.
Why do I need to set the
key
for custom columns but not for normal columns:<DataTable :value="rows"> <Column field="name" header="Name"></Column> <CustomColumn key="phone" field="phone" header="Phone"></CustomColumn> <Column field="email" header="Email"></Column> </DataTable>
If I remove the key="phone", the whole column disappears. I have been wasting so much time trying to figure out what is wrong with my code because of that. I'm leaving this here in case someone is having the same problem as I.
Hey @os-tohe!
Sorry, may be you can clear it for me, have you been able to make CustomColumn
work? For me, there is no difference, is there key
prop or not. It just doesn't work.
May you share your CustomColumn
component code here?
I am trying to achieve same, but for some reason it just doesn't work :(
May you share your
CustomColumn
component code here? I am trying to achieve same, but for some reason it just doesn't work :(
I just copied the code from the stackblitz link which is at the top of this thread: https://stackblitz.com/edit/primevue-datable-slots-customcolumnwrapper-issue?file=src%2FApp.vue
I am able to make CustomColumn
work if I am using the DataTable
from the primevue/datatable
directly and I set the key
for the CustomColumn
. If I use my custom DataTable
wrapper then even the CustomColumn
with key
does not work. Maybe you are using your DataTable
wrapper and that is why your CustomColumn
code does not work?
You can test that the key
makes a difference by opening the stackblitz link and by removing the key in App.vue
at line 48.
Ah... yes, my bad, I understood it incorrectly Yes, I have a CustomTable wrapper, and I want to have CustomColumn, which, unfortunately, doesn't work :(
I managed to make a custom column component inside a custom table component work after
- Applying the following patch (use
patch-package
or pnpm):
diff --git a/utils/utils.esm.js b/utils/utils.esm.js
index 8ca096225758e54c3418d08030ef63874caf077e..b12f6343098a2a7d08be96aa5a60cad4f61811c0 100644
--- a/utils/utils.esm.js
+++ b/utils/utils.esm.js
@@ -1200,7 +1200,7 @@ var _default = /*#__PURE__*/function () {
var components = [];
children.forEach(function (child) {
if (child.children instanceof Array) {
- components = components.concat(_this._recursive(components, child.children));
+ components = components.concat(_this._recursive(helpers, child.children));
} else if (child.type.name === _this.type) {
components.push(child);
} else if (ObjectUtils.isNotEmpty(child.key)) {
- Adding unique keys to every custom column component usage (probably should be the same as field names?).
I managed to make a custom column component inside a custom table component work after
- Applying the following patch (use
patch-package
or pnpm):diff --git a/utils/utils.esm.js b/utils/utils.esm.js index 8ca096225758e54c3418d08030ef63874caf077e..b12f6343098a2a7d08be96aa5a60cad4f61811c0 100644 --- a/utils/utils.esm.js +++ b/utils/utils.esm.js @@ -1200,7 +1200,7 @@ var _default = /*#__PURE__*/function () { var components = []; children.forEach(function (child) { if (child.children instanceof Array) { - components = components.concat(_this._recursive(components, child.children)); + components = components.concat(_this._recursive(helpers, child.children)); } else if (child.type.name === _this.type) { components.push(child); } else if (ObjectUtils.isNotEmpty(child.key)) {
- Adding unique keys to every custom column component usage (probably should be the same as field names?).
That does not seem to work with every DataTable prop. For example if I use this advanced filter, it does not render the filter menu icon on the header. This is how my Column wrapper looks like:
<template>
<Column v-bind="$props">
<template v-for="slot of slotNames" :key="slot" #[slot]="slotProps">
<!-- @vue-ignore -- For some reason slot can't infer the correct type -->
<slot :name="slot" v-bind="slotProps ?? {}"></slot>
</template>
</Column>
</template>
<script setup lang="ts">
import Column, { type ColumnProps, type ColumnSlots } from 'primevue/column';
import { computed, useSlots } from 'vue';
// Get rid of unnecessary ts errors in template by casting slots
const slots = useSlots();
const slotNames = computed(() => {
return Object.keys(slots) as (keyof ColumnSlots)[];
});
defineProps<ColumnProps>();
defineSlots<ColumnSlots>();
</script>
Do let me know if there is something wrong with my Column wrapper.
EDIT:
I noticed that if I use v-bind="$attrs"
then the filter menu icon is rendered on the header, but my props are not passed down at all anymore. If I try to combine them like this v-bind="($attrs, $props)"
or this v-bind="{ ...$attrs, ...$props }"
then only my props function correctly, so the filter menu icon is not rendered anymore.
Why was this removed from 3.53.0 milestone? Is this now fixed? We have been waiting for a fix to this for months now, please don't tell me that this is not going to be fixed at all.
I believe that I have found a working solution for the Column wrapper:
<template>
<Column v-bind="($attrs, $props)">
<template v-for="slot of slotNames" :key="slot" #[slot]="slotProps">
<!-- @vue-ignore -- For some reason slot can't infer the correct type -->
<slot :name="slot" v-bind="slotProps ?? {}"></slot>
</template>
</Column>
</template>
<script setup lang="ts">
import Column, { type ColumnProps, type ColumnSlots } from 'primevue/column';
import { computed, useSlots } from 'vue';
// Get rid of unnecessary ts errors in template by casting slots
const slots = useSlots();
const slotNames = computed(() => {
return Object.keys(slots) as (keyof ColumnSlots)[];
});
withDefaults(defineProps<ColumnProps>(), {
sortable: false,
showFilterMenu: true,
showFilterOperator: true,
showClearButton: true,
showApplyButton: true,
showFilterMatchModes: true,
showAddButton: true,
maxConstraints: 2,
excludeGlobalFilter: false,
expander: false,
rowReorder: false,
reorderableColumn: true,
rowEditor: false,
frozen: false,
alignFrozen: 'left',
exportable: false,
hidden: false,
unstyled: false,
});
defineSlots<ColumnSlots>();
</script>
That in combination with the patch provided in https://github.com/primefaces/primevue/issues/5190#issuecomment-2117361030 and adding key
prop to every column seems to function correctly. The problem with my previous solution was that I did not setup the default values for props correctly, so it set the showFilterMenu
property to false, which caused it not to be visible.
I managed to make a custom column component inside a custom table component work after
- Applying the following patch (use
patch-package
or pnpm):diff --git a/utils/utils.esm.js b/utils/utils.esm.js index 8ca096225758e54c3418d08030ef63874caf077e..b12f6343098a2a7d08be96aa5a60cad4f61811c0 100644 --- a/utils/utils.esm.js +++ b/utils/utils.esm.js @@ -1200,7 +1200,7 @@ var _default = /*#__PURE__*/function () { var components = []; children.forEach(function (child) { if (child.children instanceof Array) { - components = components.concat(_this._recursive(components, child.children)); + components = components.concat(_this._recursive(helpers, child.children)); } else if (child.type.name === _this.type) { components.push(child); } else if (ObjectUtils.isNotEmpty(child.key)) {
- Adding unique keys to every custom column component usage (probably should be the same as field names?).
In PrimeVue v4 that file is moved under @primevue
package. It is found in @primevue/core/utils/index.mjs
. You also need to install the @primevue/core
package if you want to patch it with the patch-package
.