How can I achieve custom rendering when overriding Vuetify v-data-table scoped item slots?

684 Views Asked by At
<!-- DataTable.vue -->
<template>
  <v-card outlined>
    <v-data-table
      v-bind="$attrs"
      v-on="$listeners"
      dense
    >
      <template
        v-for="(_, name) in $scopedSlots"
        #[name]="slotData"
      >
        <slot
          :name="name"
          v-bind="slotData"
        ></slot>
      </template>
    </v-data-table>
  </v-card>
</template>

Let's say every time I have a null value I want a specific render, how can I achieve this goal? I'll write some code to help you better understand my question.

  <!-- Relevant code snippet -->
  <slot
    :name="name"
    v-bind="slotData"
  >
    <template v-if="slotData.value">
      {{ slotData.value }}
    </template>
    <template v-else>
      -
    </template>
  </slot>

Edit #1

To achieve my goal I had to cycle through headers instead of scoped slots.

<!-- DataTable.vue -->
<template>
  <v-card outlined>
    <v-data-table
      v-bind="$attrs"
      v-on="$listeners"
      dense
    >
      <template
        v-for="{ value } in $attrs.headers"
        #[`item.${value}`]="slotData"
      >
        <slot
          :name="`item.${value}`"
          v-bind="slotData"
        >
          <!-- Here we can add any custom rendering logic we want -->
          <template v-if="slotData.value">
            {{ slotData.value }}
          </template>
          <template v-else>
            -
          </template>
        </slot>
      </template>
    </v-data-table>
  </v-card>
</template>

While this solution works, there are a couple of things to point out:

  1. We are relying on slot names, which might be changed and break our logic.
  2. We must reimplement some defaults. For example, arrays are now rendered like this

enter image description here

and no more like this.

enter image description here

To prevent this behaviour, we must update our code.

<!-- Relevant code snippet -->
<slot
  :name="`item.${value}`"
  v-bind="slotData"
>
  <template v-if="!slotData.value">
    -
  </template>
  <template v-else-if="Array.isArray(slotData.value)">
    {{ slotData.value.join(', ') }}
  </template>
  <template v-else>
    {{ slotData.value }}
  </template>
</slot>
1

There are 1 best solutions below

2
On

If all you want is to have some pre-configured props, instead of making a base data-table component. I would store my pre-config props as an object in a js file somewhere in my project, then I'd just import it and pass that object to the v-data-table with the v-bind property.

Kinda like what the correct answer did in this question: Add condition in vue attribute instead using v-if condition