1
votes

Say I have a custom component that uses Vuetify's v-data-table within.

Within this component, there's multiple other custom components such as loaders and specific column-based components for displaying data in a certain way.

I found myself using the same code for filtering, retrieving data, loaders etc. across the project - so not very DRY.

The things that vary are:

  1. API request url to retrieve data from (which I can pass to this generic component)

  2. headers for v-data-table (which I pass to this generic component)

  3. specific item slot templates! (One file using this same code would need a column modification like the below, requiring different components sometimes too):

      <template v-slot:[`item.FullName`]="{ item }">
        <router-link class="black--text text-decoration-none" :to="'/users/' + item.Id">
         <Avatar :string="item.FullName" />
        </router-link>
      </template>
    

    Where another would have for example:

      <template v-slot:[`item.serial`]="{ item }">
        <copy-label :text="item.serial" />
      </template>
    

    There are many more unique "column templates" that I use obviously, this is just an example.

  4. modifying items passed to v-data-table in a computed property (to add "actions" or run cleanups and/or modify content before displaying it - not related to actual HTML output, but value itself)

      computed: {
        items () {
          if (!this.data || !this.data.Values) {
            return []
          }
    
         return this.data.Values.map((item) => {
           return {
             device: this.$getItemName(item),
             serial: item.SerialNumber,
             hwVersion: this.$getItemHwVersion(item),
             swVersion: this.$getItemSwVersion(item),
             actions: [
              { to: '/devices/' + item.Id, text: this.$t('common.open') },
              { to: '/devices/' + item.Id + '/replace', text: this.$t('common.replace') }
       ],
             ...item
          }
       })
    }
    
  5. there are some unique methods that I can use on certain template slot item modifications, such as dateMoreThan24HoursAgo() below:

      <template v-slot:[`item.LastLogin`]="{ item }">
         <span v-if="dateMoreThan24HoursAgo(item.LastLogin)">{{ item.LastLogin | formatDate }}</span>
         <span v-else>
           {{ item.LastLogin | formatDateAgo }}
         </span>
       </template>
    

    I can always make this global or provide them as a prop so this point should not be a big issue.

So my questions are:

  1. What is the best way to use one component with v-data-table within but dynamically pass template slots and also allow item modification prior to passing the array to the v-data-table (as per point 3 and 4 above)
  2. is there a better way to approach this since this seems too complex (should I just keep separate specific files)? It does not feel very DRY, that's why I'm not very fond of the current solution.

Basically I would be happy to have something like:

data: () => {
    return {
      apiPath: 'devices',
      headers: [
        { text: 'Device', align: 'start', value: 'device', sortable: false, class: 'text-none' },
        { text: 'Serial Number', sortable: false, value: 'serial', class: 'text-none' },
        { text: 'Status', value: 'Status', class: 'text-none' },
        { text: 'Calibration', value: 'NextCalibrationDate', class: 'text-none' },
        { text: '', sortable: false, align: 'right', value: 'actions' }
      ],
      itemsModify: (items) => {
         return items.map((item) => {
           return {
             device: this.$getItemName(item),
             serial: item.SerialNumber,
             actions: [
              { to: '/devices/' + item.Id, text: this.$t('common.open') },
              { to: '/devices/' + item.Id + '/replace', text: this.$t('common.replace') }
              ],
             ...item
          }
        })
      },
      columnTemplatesPath: '/path/to/vue/file/with/templates' 
    }
  }

And then I'd just call my dynamic component like so:

<GenericTable 
  :api-path="apiPath" 
  :headers="headers" 
  :items-modify="itemsModify" 
  :column-templates-path="columnTemplatesPath" 
/>

Relevant but not exactly a solution to my question:

1

1 Answers

0
votes

a bit late. I found myself in the same problem that you. Googled it and found this answer that is pretty easy to implement and will solve our problem.

https://forum.vuejs.org/t/accessing-the-slots-of-a-reusable-component/72378/3