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:
API request url to retrieve data from (which I can pass to this generic component)
headers for
v-data-table
(which I pass to this generic component)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.
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 } }) }
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:
- 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) - 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: