How to use v-for to create slot content for multiple slots - vue.js

I have a table in vuetify where I want to create a template for 14 of the columns. Instead of making 14 different templates like this:
<v-data-table disable-pagination
:headers="headers"
:items="users"
:search="search"
:hide-default-footer="true"
>
<template v-slot:[`item.date_0`]="{ item }">
<ScheduleIcon :item="item.date_0" />
</template>
<template v-slot:[`item.date_1`]="{ item }">
<ScheduleIcon :item="item.date_1" />
</template>
<template v-slot:[`item.date_2`]="{ item }">
<ScheduleIcon :item="item.date_2" />
</template>
</v-data-table>
I want to make a v-for loop with an index from 0-13 and at the same time use that index-value in the slot and props variables. Something like this is pseudo-code:
<template v-slot:[`item.date_INDEX`]="{ item }" v-for="index in 13" :key="index">
<ScheduleIcon :item="item.date_INDEX" />
</template>
How would I do this? The v-for give me the following error:
<template> cannot be keyed. Place the key on real elements instead.

Your "pseudo code" is almost right...
This should work:
<template v-slot:[`item.date_${index-1}`]="{ item }" v-for="index in 14">
<ScheduleIcon :item="item[`date_${index-1}`]" :key="index" />
</template>
key is not allowed or needed on slot content (<template>) even if it is in v-for. Remove it or place it on ScheduleIcon component...

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 }">

Is it possible to integrate a clickable cell to vutify v-data-table?

Good morning,
I know that
<v-data-table
:headers="headersfav"
:items="itemsfav"
#click:row="showSaleslead"
>
can start an action when the row is clicked. Is there also the possibility to limit it to one cell?
You will have to use the relevant cell slot:
<v-data-table
:headers="headersfav"
:items="itemsfav"
#click:row="showSaleslead"
>
<template slot="items.cellName" slot-scope="{item}">
<div #click.stop="onSingleCellClick">{{ item.value }}</div>
</template>
</v-data-table>
There's no event to do that here, but you could achieve that using item slot :
<v-data-table :headers="headersfav" :items="itemsfav">
<template v-slot:item.saleslead="{ item }">
<span #click.stop="showSaleslead">{{ item.saleslead}}</span>
</template>
</v-data-table>

How to show value dynamically in vue js wth div

showing values from url dynamically, already showing in table correctly, but i want show with div.
<vs-table ref="table" multiple v-model="selected" pagination :max-items="itemsPerPage" :data="products">
<template slot-scope="{data}">
<vs-tr :data="row" :key="index" v-for="(row, index) in data">
<vs-td>
<p>{{data[index].name}}</p>
</vs-td>
</vs-tr>
</template>
</vs-table>
now checking with this div
<template slot-scope="{data}">
<div v-for="(row1, index1) in data" v-bind:key="row1.id">
{{data[index1].name}}
</div>
</template>
this div code not working
If you are running these both on the same page it might have something to do with your :keys being on the same ID. e.g. set the keys with a prefix
<div v-for="(row, index) in data" v-bind:key="'div-'+row.id">
{{data[index].name}}
</div>

vue scoped slot not being exposed

I am trying to build a stepper component, where the stepper will expose to the child component the step, so it can display certain things. However, the child component is not receiving the step.
Child component:
<template>
<v-stepper v-model="e1">
<v-stepper-header>
<template v-for="i in steps.length">
<v-stepper-step
:key="`${i}-step`"
:complete="e1 > i"
:step="i"
complete-icon="$vuetify.icons.check"
>
{{steps[i-1].text}}
</v-stepper-step>
<v-divider
v-if="i < steps.length"
:key="i"
></v-divider>
</template>
</v-stepper-header>
<v-stepper-items>
<v-stepper-content
v-for="(step, i) in steps"
:key="`$step_content_${i}`"
:step="i+1"
>
{{step}}
<slot :step="step"></slot>
<v-btn
v-if="i+1 < steps.length"
color="primary"
#click="nextStep"
:disabled="step.disabled"
>
{{$t('conditions.stepper_continue')}}
</v-btn>
<v-btn
:disabled="e1 < 2"
#click="prevStep"
flat>
{{$t('conditions.stepper_back')}}
</v-btn>
</v-stepper-content>
</v-stepper-items>
</v-stepper>
</template>
Parent:
<StepperComp v-if="mode !=='list'" :steps="steps">
<template v-if="mode !== 'list'" scoped-slot="step">
{{step}}
</template>
</StepperComp>
I am getting the following error:
Property or method "step" is not defined on the instance but referenced during render.
Why is the parent unable to access the step from the scoped slot?
It looks like you misspelled slot-scope (you incorrectly used scoped-slot). In addition, the attribute value is an object of scope data, so you would have to reference step within that object:
<template slot-scope="scopeData">
{{ scopeData.step }}
</template>
<!-- or use object destructuring -->
<template slot-scope="{ step }">
{{ step }}
</template>
The syntax above is actually deprecated since Vue 2.6.0, so you should migrate to the new recommended syntax:
<template v-slot="scopeData">
{{ scopeData.step }}
</template>
<!-- or use object destructuring -->
<template v-slot="{ step }">
{{ step }}
</template>
<!-- or v-slot shorthand: https://v2.vuejs.org/v2/guide/components-slots.html#Named-Slots-Shorthand -->
<template #default="{ step }">
{{ step }}
</template>

Vuejs nested slots: how to pass slot to grandchild

I use different vuetify components, for example v-menu. It has a template like this:
<v-menu>
<a slot="activator">menu</a>
<v-list>
<v-list-tile>Menu Entry 1</v-list-tile>
<v-list-tile>Menu Entry 2</v-list-tile>
</v-list>
</v-menu>
Suppose I want to add another wrapper around it. That is my special menu component that has some predefined menu options. And I want it to has an activator slot as well. And the last should be somehow assigned to the original v-menu activator slot. Is it possible?
Example:
// index.vue:
<template>
<my-special-menu>
<button>My special menu trigger</button>
</my-special-menu>
</template>
// MySpecialMenu.vue
<template>
<v-menu>
<slot slot="activator"/> <-- I don't know how to write this line
<v-list>...</v-list>
</v-menu>
</template>
<slot slot="activator"> is an incorrect equation. The goal is to pull the content from the parent (that is <button>..</button> in the example), and use it as slot="activator" in v-menu.
I can write it like this:
<v-menu>
<a slot="activator"><slot/></a>
...
</v-menu>
But this case the result template will be:
<div class="v-menu__activator">
<a>
<button>My special menu trigger</button>
</a>
</div>
That's not exactly what I want. Is it possible to get rid off <a> wrapper here?
Update:
We can use a construction like <template slot="activator"><slot name="activator"/></template> to throw some slot to a grand child. But what if we have multiple slots and we want to proxy them all? That's like inheritAttrs and v-bind="$attrs" for slots. Is it currently possible?
For example, there's <v-autocomplete> component in vuetify that has append, prepend, label, no-data, progress, item, selection etc slots. I write some wrapper component around this, it currently looks like:
<template>
<v-autocomplete ..>
<template slot="append"><slot name="append"/></template>
<template slot="prepend"><slot name="prepend"/></template>
<template slot="label"><slot name="label"/></template>
...
<template slot="item" slot-scope="props"><slot name="item" v-bind="props"/></template>
</v-autocomplete>
</template>
Is it possible to avoid all slots enumeration here?
If you put the slot attribute on a html element, that html element is passed to the child component to fill the slot with that name. If you don't want to pass along a html element, you can use slot on a template tag within your component. A template tag groups elements, but does not render to a html element, which is perfect here. You can use template tags also for other things, such as to group elements in a v-if for example, or to repeat multiple elements with a v-for.
// App.vue
<template>
<div id="app">
<test>
<template slot="activator">
Click <b>me</b>!
</template>
</test>
</div>
</template>
// Test.vue
<template>
<div class="wrapper">
<grand-child>
<template slot="activator">
<slot name="activator"></slot>
</template>
</grand-child>
This is some text
</div>
</template>
// GrandChild.vue
<template>
<div>
<a href="#" #click="toggle = !toggle">
<slot name="activator">Default</slot>
</a>
<div v-if="toggle">This appears and disappears</div>
</div>
</template>
Edit: If you want to do this for arbitrary slots, this is also possible. this.$slots contains the slots and their content, so with something like the following, you can pass the slot content to a slot with the same name:
<grand-child>
<template v-for="(_, slot) in $slots">
<template :slot="slot">
<slot :name="slot"></slot>
</template>
</template>
</grand-child>
For completeness sake, scoped slots can be accessed through $scopedSlots and be propagated like so:
<grand-child>
<template v-for="(_, slot) in $scopedSlots" v-slot:[slot]="props">
<slot :name="slot" v-bind="props" />
</template>
</grand-child>
source and comment
I had EsLint errors because of the depreciated :slot and $scopedSlots attributes.
So I combined both of #Sumurai8 answers like this and it works great:
<template v-for="(_, slot) in $slots" v-slot:[slot]>
<slot :name="slot"></slot>
</template>
If you have both named and unnamed slots with props:
Vue 3
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData || {}" />
</template>
Typescript version
<template v-for="(_, name) in ($slots as {})" #[name]="slotData">
<slot :name="name" v-bind="slotData || {}" />
</template>