primefaces / primevue

Next Generation Vue UI Component Library
https://primevue.org
MIT License
9.99k stars 1.2k forks source link

Add custom wrapper support for helper components on DataTable and TreeTable #4646

Closed mertsincan closed 9 months ago

mertsincan commented 11 months ago

Currently, primevue components such as DataTable need a helper component within themselves. These helper components must be directly child components. But sometimes users may need to wrap them with a custom wrapper instead of using them directly as children. Exp;

<DataTable :value="products" tableStyle="min-width: 50rem">
     <MyColumn key="uid_1" field="code" header="Code"></MyColumn>
     <MyColumnWrapper key="uid_2" :fields="['name', 'category']"></MyColumnWrapper>
     <Column field="quantity" header="Quantity"></Column>
</DataTable>

🚨 Note: Users must use a key prop with a unique value to render helper components in the correct order in the custom wrapper components.


Affected Components; Components Helper Components
✅ DataTable Column, Row, ColumnGroup
✅ TreeTable Column
moyoakala commented 9 months ago

@mertsincan, would this also work for DataTables with expandable rows?

<template>
  <CustomDataTable 
    v-model:expandedRows="expandedRows"
    :value="products"
    data-key="name"
  >
    <Column expander />
    <CustomColumn field="price" header="Price" sortable>
      <template #body="product">
        {{ product.data.price }}
      </template>
    </CustomColumn >
    <template #expansion="subrow">
      <CustomDataTable :value="subrow.data.sub_rows" data-key="name">
        <CustomColumn  field="price" header="Price">
          <template #body="subrow">
            {{ subrow.data.price }}
          </template>
        </CustomColumn >
      </CustomDataTable >
    </template>
  </CustomDataTable >
</template>
mertsincan commented 9 months ago

Hi @moyoakala yes, you can use it.

hackjackhack commented 8 months ago

Why is this commit reverted may I ask?

mertsincan commented 8 months ago

I only reverted my fixes for TabView and Accordion. Since they do not contain default slots, it was not technically possible to establish a structure like DataTable and TreeTable. We plan to make improvements to them in future versions. Currently, you can create custom wrapper components for DataTable and TreeTable.

Best Regards,

hackjackhack commented 8 months ago

Cool. I was trying to create custom dynamic async tabpanel, which are loaded based on runtime conditions without v-if.Will the future improvement of tabview cover this?

fhnaumann commented 8 months ago

I only reverted my fixes for TabView and Accordion. Since they do not contain default slots, it was not technically possible to establish a structure like DataTable and TreeTable. We plan to make improvements to them in future versions. Currently, you can create custom wrapper components for DataTable and TreeTable.

Best Regards,

Do you know a way to create custom wrapper components for Accordion (including AccordionTab)? Was really looking forward to that.

mertsincan commented 8 months ago

Unfortunately, there is no way for now :/ I plan to make improvements to them in future versions.

oliviermattei commented 6 months ago

I'm trying a similar approach to create a custom multiple selection column. But nothing is rendered

TriStateSelectionColumn.vue

<script lang="ts" setup>
const emits = defineEmits(['select'])
const selectionMode = ref(null)
</script>

<template>
  <Column
    :pt="{
      headerCheckbox: {
        class: 'hidden',
      },
    }"
    header-style="width: 3rem"
    selection-mode="multiple"
  >
    <template #header>
      <TriStateCheckbox
        v-model="selectionMode"
        @update:model-value="emits('select', $event)"
      >
        <template #checkicon>
          <Icon
            color="white"
            icon="minus"
          />
        </template>
        <template #uncheckicon>
          <Icon
            color="white"
            icon="check"
          />
        </template>
      </TriStateCheckbox>
    </template>
  </Column>
</template>
<DataTable 
    :selection="selectedValues"
    :value="values"
>
    <TriStateSelectionColumn
         key="uid"
         @select="onSelectChange"
     />
</Datatable>

but nothing is rendered, Have you and idea ?

nikolay-kornev commented 4 months ago

@mertsincan Could you please provide example of working wrapper component for Column? I cannot make it render, no matter what I do. In my specific case, I am creating a custom UI library. I wrap PrimeVue components as described here - https://tailwind.primevue.org/guides/building-ui-library/.

I have wrapped DataTable, and Column components. When I try using them, all I see is pagination. Columns are not rendering.

luizottavioc commented 3 months ago

I'm having the same problem. My custom columns are not rendered in the DataTable component

claushellsing commented 3 months ago

Same thing happen to me

Thiritin commented 3 months ago

Okay... I have run into the same issue. It looks like it works if the Custom Component has a v-if Attached to it.

image image

(If adding a v-if to the upc it will be visible again)

luizottavioc commented 3 months ago

In my case, v-if has no effect... code preview

luizottavioc commented 3 months ago

I did some tests here and I found a solution to temporarily resolve it.

In my structure, I have a "DefaultTable" component and I just pass the columns to it, then my table columns are not inside the Datatable component.

To make the custom columns rendered by the thrid component, I need to import them into my DefaultTable using the v-if="true" condition and adding a display: none to don't show them by default, like this:

// DefaultTable.vue
<template>
  <TableLoading v-if="tableData === null" />
  <div
    v-else
    class="w-full overflow-y-auto"
  >
    <DataTable
      ref="dt"
      :value="tableData"
      [...]
    >
      <template #header>[...]</template>
      <StringColumn v-if="true" field="" header="" class="hidden" />
      <ClassColumn v-if="true" field="" header="" class="hidden" />
      <BooleanColumn v-if="true" field="" header="" class="hidden" />
      [...]
      <slot name="columns"></slot>
    </DataTable>
  </div>
</template>

<script lang="ts" setup>
import StringColumn from '@/components/tables/columns/default/StringColumn.vue'
import ClassColumn from '@/components/tables/columns/default/ClassColumn.vue'
import BooleanColumn from '@/components/tables/columns/default/BooleanColumn.vue'
[...]
</script>

In the component where my table is built, i just pass the custom columns into the column slot reusing the v-if="true" like this:

//UsersPage.vue
<template>
  <DefaultTable url="/users" [...]>
    <template #columns>
      <StringColumn
        v-if="true"
        field="idUser"
        header="ID"
      />
      <BooleanColumn
        v-if="true"
        field="online"
        header="Connection Status"
        true-text="Online"
        false-text="Offline"
      >
        <template #body="{ data }">
          <UserConnectionStatusChip :is-online="data.online" />
        </template>
      </BooleanColumn>
      <ClassColumn
        v-if="true"
        field="companyBranch"
        header="Company Branch"
        :options="metadata.companyBranches"
      />
    </template>
  </DefaultTable>
</template>

<script lang="ts" setup>
import DefaultTable from '@/components/tables/DefaultTable.vue'
import StringColumn from '@/components/tables/columns/default/StringColumn.vue'
import BooleanColumn from '@/components/tables/columns/default/BooleanColumn.vue'
import ClassColumn from '@/components/tables/columns/default/ClassColumn.vue'
</script>
[...]
Thiritin commented 3 months ago

I did some tests here and I found a solution to temporarily resolve it. [...]

Oh my god, thank you. That is a smart solution to overcome this issue.

anthonyespirat commented 2 months ago

still got the issue on 4.0.0

NodeAdam commented 1 month ago

So is v-if="true" the solution, or is it a workaround? Will there be an actual fix?

Real-Gecko commented 2 weeks ago

Any working example of this?

Ciriak commented 1 week ago

So is v-if="true" the solution, or is it a workaround? Will there be an actual fix?

Well at least it works but it feels very unconventional

@Real-Gecko

DataTable.vue

<DataTable>
    <StatusColumn
      v-if="true"
      field="status"
      header="status" />
</DataTable>

StatusColumn.vue

<template>
<Column
    v-bind="props"
  >
    <template #body="{ data }">
      {{data}}
    </template>
      </Column>
</template>

<script setup lang="ts">
import type { ColumnProps } from 'primevue/column';
import Column from 'primevue/column';
const props = defineProps<ColumnProps>();
</script>
Real-Gecko commented 1 day ago
<DataTable>
    <StatusColumn
      v-if="true"
      field="status"
      header="status" />
</DataTable>

It works if column is a direct descendant of DataTable but does not work for nested children like:

// SomethingList.vue
  <List>
    <LinkColumn v-if="true" field="title" header="Title />
  </List>
// List.vue
    <DataTable>
      <slot></slot>
    </DataTable>