I have a table component that takes a 'variant' prop which simply dictates some css styles to apply. I use it as follows:
<BaseTable :variant="'with_background'" :columns="[{ name: 'Datum' }, { name: 'Bedrag', alignment: 'right' }, { name: 'Verkoper' }, { name: 'Status' }]">
<BaseTableRow v-for="(pastOrder, index) in pastOrders" :key="index" :variant="'with_background'">
<BaseTableCell :variant="'with_background'">{{ pastOrder.date }}</BaseTableCell>
<BaseTableCell :variant="'with_background'" :alignment="'right'">€{{ pastOrder.amount }}</BaseTableCell>
<BaseTableCell :variant="'with_background'">{{ pastOrder.supplier }}</BaseTableCell>
<BaseTableCell :variant="'with_background'">
<BaseBadge :variant="pastOrder.status.variant">{{ pastOrder.status.text }}</BaseBadge>
</BaseTableCell>
</BaseTableRow>
</BaseTable>
As you can see, the variant prop is also required by the table rows and cells. The table rows simply fill a default <slot></slot> in BaseTable.vue. I would love to pass the variant property to the items that are rendered in that slot, so that I would only have to change the variant on BaseTable to change it on all subcomponents. Is this possible? Existing questions seem to discuss the usage of slot properties in the parent component, but then I would still have to add it to every cell whenever I create a table.
Thanks in advance!
If I understand correctly, the following is probably currently the best option:
<BaseTable :variant="'with_background'" v-slot="{ variant }" :columns="[{ name: 'Datum' }, { name: 'Bedrag', alignment: 'right' }, { name: 'Verkoper' }, { name: 'Status' }]">
<BaseTableRow v-for="(pastOrder, index) in pastOrders" :key="index" :variant="variant">
<BaseTableCell :variant="variant">{{ pastOrder.date }}</BaseTableCell>
<BaseTableCell :variant="variant" :alignment="'right'">€{{ pastOrder.amount }}</BaseTableCell>
<BaseTableCell :variant="variant">{{ pastOrder.supplier }}</BaseTableCell>
<BaseTableCell :variant="variant">
<BaseBadge :variant="pastOrder.status.variant">{{ pastOrder.status.text }}</BaseBadge>
</BaseTableCell>
</BaseTableRow>
</BaseTable>
And then in the underlying components, I use <slot :variant="variant"></slot> to pass it onto the rows and cells. I do still wonder if that is possible without specifying :variant="variant" on every cell in the parent component every time I use this table..
Related
thanks for reading my question. I've a dynamic table and I'm trying to use a prop in v-slot, like bellow:
<v-data-table :headers="chooseHeader" :items="chooseItem" :search="search">
<template v-slot:[`item.iconValue`]>
<v-icon> mdi-clipboard-edit-outline </v-icon>
</template>
</v-data-table>
<script>
export default {
name: 'EasyHrPerformanceEvaluationListsNewSolicitation',
props: {
iconValue: {
type: Object,
required: false,
},
chooseHeader: {
type: Array,
required: true,
},
chooseItem: {
type: Array,
required: true,
},
},
I call the component EasyHrPerformanceEvaluationListsNewSolicitation to pass the prop values:
<EasyHrPerformanceEvaluationListsNewSolicitation
:title="$t('Novas Avaliações')"
:chooseHeader="selfEvaluationsHeader"
:chooseItem="selfEvaluations"
:iconValue="makeEvaluation" //v-slot value
>
</EasyHrPerformanceEvaluationListsNewSolicitation>
Bellow is my page, if you have a look you can se the column "avaliar" isn't working (my v-slot)
enter image description here
Is possible to use the prop value bellow in my v-slot?
iconValue: {
type: Object,
required: false,
},
Can you help me?
I think from the syntax you are using vuetify data table. And as per the doc's, slots are to be written like this.
<template v-slot:[`item.iconValue`]="{item}">
Refer -> https://vuetifyjs.com/en/components/data-tables/#item
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:
Is it possible to use dynamic scoped slots to override column values inside <v-data-table>?
Dynamically building a table using vuetifyJS data table
I have put a q-table in my page to show my data. this is work correctly. I want to put an href in first column and another in last column. You should can click on them to go to other pages for showing details row and edit row Respectively.
Title: go to page /#/offer/uuid
Edit: go to page /#/account/offers/edit/uuid
<template>
<q-page>
<div class="row justify-center">
<q-table
:data="data"
:columns="columns"
row-key="id"
selection="single"
:selected.sync="selected"
:filter="filter"
:loading="loading"
>
</q-table>
</div>
</div>
</q-page>
</template>
<script>
.....
columns: [
{
name: 'title',
required: true,
label: 'Title',
align: 'left',
field: row => row.title,
sortable: true,
format: val => '' + val + '' //this is not work
},
{
name: 'category',
label: 'Category',
field: 'category',
sortable: true
},
{
name: 'payment',
label: 'Payment',
field: 'payment'
},
{
name: 'action',
label: 'Edit',
field: 'key',
format: val => '' + val + '' //this is not work
}
]
.....
</script>
This answer assumes Quasar 1.0.0-rc4. For earlier versions it may look a bit different but the crux of it is that you need to use scoped slots.
There are scoped slots called body-cell-[name] that can be used to render content that isn't plain text. Here the [name] portion should match the name for the corresponding column in your columns definition.
<q-table ...>
<template v-slot:body-cell-title="cellProperties">
<q-td :props="cellProperties">
{{ cellProperties.value }}
</q-td>
</template>
<template v-slot:body-cell-action="cellProperties">
<q-td :props="cellProperties">
{{ cellProperties.value }}
</q-td>
</template>
</q-table>
My use of v-slot assumes Vue 2.6.0+, for earlier versions you would use slot and slot-scope instead.
You haven't explained where the UUID part of your URL comes from so I haven't included that in the code above. I would imagine it is included somewhere in the row data, so in practice you would need something like :href="'#/offer/' + encodeURIComponent(cellProperties.row.uuid)".
If you're using a routing library such as Vue Router then there are alternatives to building URLs directly within <a> tags. You may wish to investigate further before sprinkling hand-crafted URLs throughout your application code. However, the use of scoped slots is likely to remain no matter how you implement your links.
Use :href. For example:
<a :href="'/app/product/'+props.row.productId">{{ props.row.name }}</a>
in my data object
items: [
{ name: "Breakfast", comp: "breakfastItems" },
{ name: "Lunch", comp: "lunchItems" },
{ name: "Dinner", comp: "dinnerItems" },
{ name: "Dessert", comp: "desertItems" }
]
where comp is a computed property.
in my component template I want to achive something like this using a for loop.
<span v-for="n in items">
{{n.comp}}
</span>
this doesn't work because I need to add {{}} when it's rendering. How do I do that?
To bind computed properties inside your template via dynamic interpolation, you can use the $root variable.
Assuming that the comp properties you've listed are collections underneath, the template might look like:
<span v-for="n in items">
<span v-for="m in $root[n.comp]">{{ m }}</span>
</span>
Here's a demonstration of the suggestion.
I need to display the item.title outside the <v-carousel> but I get this error message:
[Vue warn]: Property or method "item" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
I checked the results from the Stackoverflow search but I really struggle to understand it. I would be grateful if somebody could explain it to me by this example.
Here is my code:
<v-carousel>
<v-carousel-item v-for="(item,i) in items" v-bind:src="item.src" :key="i">
</v-carousel-item>
</v-carousel>
<h1>TITLE: {{ item.title }}</h1>
<script>
export default {
data () {
return {
items: [
{
src: '/static/a.jpg',
title: 'A',
text: 'texta'
},
{
src: '/static/b.jpg',
title: 'B',
text: 'textb'
}
{
}
}
}
</script>
This is what I need to archive:
As soon as an image changes to the next one - the a text outside of the scope should change too. I tried to check the value of the item array outside the scope but it didn't work:
<h1 v-if="(item,i) === 1">Lion</h1> <h1 v-if="(item,i) === 2">Ape</h1>
How to access the value of the current carousel item outside of the scope?
You need to add v-model on v-carousel component:
<v-carousel v-model="carousel">
<v-carousel-item
v-for="(item,i) in items"
v-bind:src="item.src"
:key="i"
></v-carousel-item>
</v-carousel>
//then set title like this:
<h1>TITLE: {{ items[carousel].title }}</h1>
And add carousel variable to data
data () {
return {
carousel: 0, //like this
items: [
...