vue.jsvuejs3primevuevue-data-tables

Primevue datatable, how to hide a row which has no data in expanded row


I am using primevue datatable to show my data, and row expansion to show additional details. https://primevue.org/datatable/#row_expansion

My problem is that some of the rows have no data in expanded row (blue T-shirt in example), and I would love this whole row to be hidden.
Lets say I have no control over data I receive, I can just edit frontend.

Example what I have: https://codesandbox.io/p/sandbox/primevue-demo-forked-3sn4wz

So I want that the row with blue T-shirt would be completely hidden/gone. Is there any easy way to do that? I tried doing that in CSS but I can hide expanded row which contains no data (using has() selector) but I cannot get rid of the main row: enter image description here

In the future I plan to use it to add filtering, and once user filters out everything from details, I want to hide main row as well

My try with CSS was:

.p-datatable-row-expansion:has(.p-datatable-emptymessage) {
  display: none;
}

EDIT: My newest try was to watch for filter variable change, and then just hide rows using javascript. Unfortunately hiding occurs before filtering, leaving sometimes empty expanded rows, and hiding them one input later.
Part of code I added:

watch(filters.value["global"], (newValue, oldValue) => {
  console.log(newValue, oldValue);
  xxxx();
});

Is there a filtering callback function after its done?
Code: https://codesandbox.io/p/sandbox/primevue-demo-forked-844j47


Solution

  • a filter will solve this problem:

    <template>
      <div class="card">
        <DataTable
          v-model:expandedRows="expandedRows"
          :value="products?.filter((item) => item.orders.length > 0)"
          dataKey="id"
          tableStyle="min-width: 60rem"
        >
        ...
    

    if you want to filter advanced functions based on filters like PrimeVue you have to add it:

    import { FilterMatchMode, FilterService } from "primevue/api";
    import { ObjectUtils } from 'primevue/utils';
    
    // your filters
    const filters = ref({
      global: { value: null, matchMode: FilterMatchMode.CONTAINS },
      name: { value: null, matchMode: FilterMatchMode.STARTS_WITH },
      "country.name": { value: null, matchMode: FilterMatchMode.STARTS_WITH },
      representative: { value: null, matchMode: FilterMatchMode.IN },
      status: { value: null, matchMode: FilterMatchMode.EQUALS },
      verified: { value: null, matchMode: FilterMatchMode.EQUALS },
    });
    
    const productsFilterFn = ({ orders }) => {
      if (!orders.length) return false
      
      return orders.some(order => {
        for (const prop in order) {
          const type = filters.value[prop] ?? filters.value.global
          if (type.matchMode === FilterMatchMode.EQUALS)
          return ObjectUtils.resolveFieldData(order, prop) === type.value
          
          if (FilterService.filters[type.matchMode](ObjectUtils.resolveFieldData(order, prop), type.value)) {
            return true
          }
        }
        return false 
      })
    }
    
    

    and use fn:

    <template>
      <div class="card">
        <DataTable
          v-model:expandedRows="expandedRows"
          :value="products?.filter(productsFilterFn)"
          dataKey="id"
          tableStyle="min-width: 60rem"
        >
        ...