vue.js how do I make a v-slot template dynamic? - vue.js

Hello and thank you for reading my question! I am working with Vue.js, Vuetify and v-data-table and I am working on making my v-slot work with two different strings as the name of the header.
<template>
<v-container fluid>
<v-data-table
:options="data"
:headers="headers"
:items="data
:server-items-length="40"
:loading="loading"
:multi-sort="true"
#update:options="updateThePage"
>
<template v-slot:[`header.overalls`]="{ header }" class="flight-date-header">
<overallDatePicker
:options="data"
/>
{{ header.name }}
</template>
<template v-slot:[`item.name`]="{ item }">
<p class="company-name">
{{ item.companyName }}
</p>
</template>
<template v-slot:[`item.price`]="{ item }">
<p> {{ item.price }} </p>
</template>
<template v-slot:[`item.flightDate`]="{ item }">
<p class="style">
{{ item.startDate }}
</p>
</template>
</v-data-table>
</v-container>
</template>
and I store my headers like below
headers: [
{ text: 'Campaign Name', value: 'overalls' },
],
Ideally I would like the name of this slot
<template v-slot:[`header.overalls`]="{ header }" class="flight-date-header">
<overallDatePicker
:options="data"
/>
</template>
To work with two different data options. right now the name of the header is overalls but I want the name of the header so ['header.overalls'] to be like ['header.('overalls' || 'shoes')] ,
The reason I am doing this is right now when I click on the header the options.sortBy property of the table gets set to 'overalls', but I want the icon on the column to show up if the options.sortBy property of the table is "overalls" and also show up if it is "shoes"
Please help and thank you so much!

To reuse the slot content for multiple headers/columns, use Vue's dynamic slots names feature + v-for
data() {
return {
specialHeaders: ['overalls', 'shoes'],
}
}
<template v-for="column in specialHeaders" v-slot:[`header.${column}`]="{ header }">
Special:{{header.text}}
</template>

Related

Vue JS what does # do inside a <template> does in v-data-table?

I am new to vue js, i was given an project example to study, i want to ask what <template #['item.amtv_start_sales_date'] = "{item}'> actually do inside . Any help is appreciated thanks
<v-data-table :headers="headers" :items="data" :search="search">
<template #[`item.amtv_start_sales_date`]="{ item }">
<div
:class="
checkDateActiveColorSales(
item.amtv_start_sales_date,
item.amtv_end_sales_date
)
"
#click="click(item.amtv_start_sales_date)"
style="font-weight: 500"
>
{{ item.amtv_start_sales_date }}
</div>
</v-data-table>
According to official docs
v-slot has a dedicated shorthand #, so <template v-slot:header> can be shortened to just <template #header>. Think of it as "render this template fragment in the child component's 'header' slot"
So <template #[`item.amtv_start_sales_date`]="{ item }"> is originally written as <template v-slot[`item.amtv_start_sales_date`]="{ item }">

How can I pass a v-data-table row information to child element

I have creating a list of users want to add them tags. I am using a data-table to display them and a combo box using chips to add or remove tags. How can I pass the user information to the method called when I add / remove a tag? Here is my code:
<v-data-table :headers="headers" :items="usersInfos" :search="search" :items-per-page="-1">
<template v-slot:[`item.tags`]="{ item }">
<v-combobox v-model="item.tags" :items="roles" chips clearable label="RĂ´les" multiple>
<template v-slot:selection="{ attrs, item, select, selected }">
<v-chip
v-bind="attrs"
:input-value="selected"
close
#click="select"
#click:close="removeRole(item)"
>
{{ item }} <!-- the tag -->
</v-chip>
</template>
</v-combobox>
</template>
</v-data-table>
Don't know if I understood it correctly but you can do following pass your item with your click event to your methods - call the function and use the passed parameter there.
in your template
#click=getSelectedItem(usersInfos)
in your script
methods: {
getSelectedItem(usersInfos) {
//do code here
console.log(usersInfos)
}
}
and than you have to use this where you have written your child element:
<child :usersInfo="usersInfo">
and in your child.vue you have to set props in your script like this:
props: ["usersInfo"]

vuetify v-select multiple text values

i am trying to set mutliple text items in a v-select.
at the moment this is my v-select and its working with one :item-text.
But i need to display two fields. so i was checking the documentation and tried to use slots.
<v-select
:disabled="isReadOnly()"
v-show="!isHidden()"
:label="getLabel()"
:hint="field.description"
:items="selectVal"
:item-value="field.options.select.ValueField"
:item-text="field.options.select.TextField"
:multiple="isType(FieldType.ManyToMany)"
:chips="isType(FieldType.ManyToMany)"
v-model="fieldValue"
:rules=rules()
return-object
>
</v-select>
But when i am doing this:
<template slot="selection" slot-scope="data">
{{ data.item.name }} - {{ data.item.description }}
</template>
<template slot="item" slot-scope="data">
{{ data.item.name }} - {{ data.item.description }}
</template>
the default behavior of v-select must be reimplemented. (chips, displaying checkboxes on multiple select....
is there another way to do this? without copying the default behaviors und have duplicated code for this simple task?
thanks in advance.
sorry for the beginner question.
item-text can also be a function so you can do something like this
<v-select
:disabled="isReadOnly()"
v-show="!isHidden()"
:label="getLabel()"
:hint="field.description"
:items="selectVal"
:item-value="field.options.select.ValueField"
:item-text="getFieldText"
:multiple="isType(FieldType.ManyToMany)"
:chips="isType(FieldType.ManyToMany)"
v-model="fieldValue"
:rules=rules()
return-object
>
</v-select>
methods:
{
getFieldText (item)
{
return `${item.name} - ${item.description}`
}
}

Condition on template with v-if using v-slot prop

I'm trying to make a condition to enable a named slot like this:
<template v-slot:item="{ item }" v-if="item.loading">
<v-progress-circular indeterminate color="primary"></v-progress-circular>
</template>
My use case is a Vuetify datatable: each item has a "loading" property, and I'd like to activate "item" slot only if the row is loading ("item" slot is Slot to replace the default rendering of a row)
The error is that item is undefined in the v-if, which seems logic : item is only defined for template children tag.
Is there a way to solve this problem?
You can filter the items that you pass to the datatable with a computed property.
Can you just not swap element based on loading ?
Vue.config.devtools = false;
Vue.config.productionTip = false;
var app = new Vue({
el: '#app',
data: {
items: [{data : "", loading: true}, {data : "Some data", loading: false}]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="item in items">
<div>
<div v-if="item.loading">
Loading...
</div>
<div v-else>
{{item.data}}
</div>
</div>
</div>
</div>
I had a similar problem, and I solved it in Vuetify 2 by importing VDataTable/Row as 'v-data-table-row', and using it to render 'regular' table rows, and for custom rows I used my own template.
JavaScript
import Row from 'vuetify/lib/components/VDataTable/Row.js';
export default {
components: { 'v-data-table-row': Row },
data() {
return {
currentItemName: 'Ice cream sandwich'
}
}
// headers, items, etc...
}
HTML
<template v-slot:item="{ item }">
<tr v-if="item.name == currentItemName" class="blue-grey lighten-4">
<td>Custom prefix - {{ item.name }}</td>
<td colspan="2">{{ item.calories }} - Custom suffix</td>
</tr>
<v-data-table-row v-else :headers="headers" :item="item">
<template
v-for="(index, name) in $scopedSlots"
v-slot:[name.substr(5)]="data"
>
<slot
v-if="name.substr(0, 5) === 'item.'"
:name="name"
v-bind="data"
></slot>
</template>
</v-data-table-row> </template
You can check out working example here.
You can just put the v-if on the child element
<template #item="{ item }">
<v-progress-circular
v-if="item.loading"
color="primary"
indeterminate
></v-progress-circular>
</template>

Vuetify concatenate two fields in v-select's item-text

Is there a way to concatenate two fields together in a v-select's item-text field?
I have it working for the drop down list values, but the visible entry doesn't show the two fields.
Problem is here: item-text="'${data.item.name}, ${data.item.group}'"
Code:
`<v-select label="Select" v-bind:items="people" v-model="e11"
item-text="'${data.item.name}, ${data.item.group}'"
item-value="name" max-height="auto" autocomplete >
<template slot="item" slot-scope="data">
<v-list-tile-content>
<v-list-tile-title
v-html="`${data.item.name}, ${data.item.group}`">
</v-list-tile-title>
<v-list-tile-sub-title
v-html="data.item.group">
</v-list-tile-sub-title>
</v-list-tile-content>
</template>
</v-select>`
Pen Example: https://codepen.io/anon/pen/dJveWM?editors=1010
Thank you
You need to define a template not only for the slot item but also for the slot selection when using <v-select>:
<template slot="selection" slot-scope="data">
<v-chip
close
#input="data.parent.selectItem(data.item)"
:selected="data.selected"
class="chip--select-multi"
:key="JSON.stringify(data.item)"
>
<v-avatar>
<img :src="data.item.avatar">
</v-avatar>
{{ data.item.name }}
</v-chip>
</template>
See
https://vuetifyjs.com/components/selects#6-scoped-slots
for reference.
This can also be a much simpler solution like the one you're trying to achieve:
<template slot="selection" slot-scope="data">
{{ data.item.name }}, {{ data.item.group }}
</template>
Also see it in action:
https://codepen.io/anon/pen/PEpaMM?editors=1011
For David Folkner: you can add the :filter="customFilter" property to the autocomplete component and then add in the methods block the customFilter function that achieves the custom search.
For example, if your items list is composed by n item objects with id, function and description properties, you should do this for searching through both function and description properties:
Autocomplete component:
<v-autocomplete v-model="itemSelected" :items="items" required box chips label="Select an Item" item-value="id" item-text="description" :filter="customFilter">
<template slot="selection" slot-scope="data">
<v-chip :selected="data.selected" class="chip--select">
{{data.item.function}} - {{ data.item.description }}
</v-chip>
</template>
<template slot="item" slot-scope="data">
<v-list-tile-content>
<v-list-tile-title v-html="data.item.function +' - '+ data.item.description"></v-list-tile-title>
</v-list-tile-content>
</template>
</v-autocomplete>
methods:
methods: {
customFilter (item, queryText, itemText) {
const textOne = item.function.toLowerCase()
const textTwo = item.description.toLowerCase()
const searchText = queryText.toLowerCase()
return textOne.indexOf(searchText) > -1 || textTwo.indexOf(searchText) > -1
}
}
<v-select
:items="unitTypes"
item-text="value"
item-value="id"
v-model="formData.unit_type"
prepend-icon="mdi-ammunition"
label="Unit Types"
required
:error-messages="errors"
>
<template slot="selection" slot-scope="data">
{{ data.item.value }} {{ data.item.type }}
</template>
<template slot="item" slot-scope="data">
{{ data.item.value }} {{ data.item.type }}
</template>
</v-select>
We are using
"vue-cli-plugin-vuetify": "2.0.5",
"vue-eslint-parser": "^7.10.0",
"vue-template-compiler": "^2.6.11",
And the following worked well for us.
<v-autocomplete v-model="data.alarmInfoId" :items="dbQueryResult" item-value="prop3" :item-text="(row) => {return row.prop1+ ' - ' + row.prop2;}"/>
dbQueryResult is a list of items returned from an API call
prop1, prop2, and prop3 are properties on each row in dbQueryResult
I believe that this will work with v-select as well. Though we use v-autocomplete so that users can type search in larger lists.
use slot="selection"
<template slot="selection" slot-scope="item">
{{ item.name }}-{{ item.group }}
</template>
To know more slot
https://vuetifyjs.com/en/api/v-autocomplete/#slots
item-text can accept a function
:item-text="item => `${item.first_value} ${item.second_value}`"