How can I properly enable slots and scoped slots usage when wrapping a Vuetify component? - vuejs2

I currently do this to enable slots and scoped slots usage.
<template>
<v-text-field
v-bind="computedAttrs"
v-on="$listeners"
>
<!-- Cycling through slots -->
<template v-for="(_, name) in $slots">
<template :slot="name">
<slot :name="name"></slot>
</template>
</template>
<!-- Cycling through scoped slots -->
<template
v-for="(_, name) in $scopedSlots"
#[name]="data"
>
<slot
:name="name"
v-bind="data"
></slot>
</template>
</v-text-field>
</template>
I don't understand why I cannot do this to enable slots usage.
<template>
<v-text-field
v-bind="computedAttrs"
v-on="$listeners"
>
<!-- Cycling through slots -->
<template
v-for="(_, name) in $slots"
#[name]
>
<slot :name="name"></slot>
</template>
<!-- Cycling through scoped slots -->
<template
v-for="(_, name) in $scopedSlots"
#[name]="data"
>
<slot
:name="name"
v-bind="data"
></slot>
</template>
</v-text-field>
</template>
I saw people doing this, but I don't understand why it doesn't work for me. What am I doing wrong?
Note: I'm using Vue2.

Related

Cascading wrapper components in Vue 3

Given the following "wrapper component" DefaultButton for a Quasar QBtn, I make use of v-bind="$attrs" and a v-for loop for passing down the slots.
DefaultButton.vue
<template>
<q-btn v-bind="$attrs" color="primary" outline rounded>
<template v-for="(_, slot) in $slots" #[slot]="scope">
<slot :name="slot" v-bind="scope || {}" />
</template>
</q-btn>
</template>
<script setup></script>
Now if I'd like to create another wrapper for DefaultButton.vue, is it necessary to repeat inserting v-bind="$attrs" and the slot template?
SomeIconButton.vue
<template>
// Is this sufficient?
<default-btn icon="some-icon"></default-btn>
// Or is this required?
<default-btn v-bind="$attrs" icon="some-icon">
<template v-for="(_, slot) in $slots" #[slot]="scope">
<slot :name="slot" v-bind="scope || {}" />
</template>
</default-btn>
</template>
<script setup />
</script>

Vue components: Using slots within slots

I have v-data-table as its own component to make it more generic for my components. I would like to have the option to provide that component how each item in the table is formatted. That would be done with a slot. But the problem is, that the provided slot has a slot for v-data-table. See the example, this is difficult to explain.
In my DataTable component I have:
<v-data-table
:headers="tableHeaders"
:items="items"
:item-key="itemKey"
:loading="isLoading"
v-model="selectedRows"
show-select
>
<!-- What do i put here? -->
</v-data-table>
And I use that data table from my other component like this:
<data-table
:table-headers="tableHeaders"
:items="messages"
:is-loading="isLoading"
:selected-rows="selectedRows"
:item-key="'MessageID'"
>
<template v-slot:item.actions="{ item }">
<v-btn :to="`messages/${item.MessageID}`" icon>
<v-icon>mdi-chevron-right</v-icon>
</v-btn>
</template>
<template v-slot:item.CreatedDateTime="{ item }">
<span>{{ format(new Date(item.CreatedDateTime)) }}</span>
</template>
</data-table>
I need the result to be rendered as:
<v-data-table
:headers="tableHeaders"
:items="items"
:item-key="itemKey"
:loading="isLoading"
v-model="selectedRows"
show-select
>
<template v-slot:item.actions="{ item }">
<v-btn :to="`messages/${item.MessageID}`" icon>
<v-icon>mdi-chevron-right</v-icon>
</v-btn>
</template>
<template v-slot:item.CreatedDateTime="{ item }">
<span>{{ format(new Date(item.CreatedDateTime)) }}</span>
</template>
</v-data-table>
So the problem is, how do I provide the templates within data-table tag to be rendered within v-data-table tag?
I had the exact same issue. I found a solution utilizing the $scopedSlotes object, like this:
<v-data-table
:headers="tableHeaders"
:items="items"
:item-key="itemKey"
:loading="isLoading"
v-model="selectedRows"
show-select
>
<template
v-for="slot in Object.keys($scopedSlots)"
:slot="slot"
slot-scope="scope"
>
<slot
:name="slot"
v-bind="scope"
></slot>
</template>
</v-data-table>
You will have to re-expose the slots in your "wrapper" component:
<v-data-table
:headers="tableHeaders"
:items="items"
:item-key="itemKey"
:loading="isLoading"
v-model="selectedRows"
show-select
>
<template v-slot:body="bodyParams">
<slot name="body" v-bind="bodyParams" />
</template>
<template v-slot:expanded-item="expandedParams">
<slot name="expanded-item" v-bind="expandedParams" />
</template>
<template v-slot:header="headerParams">
<slot name="header" v-bind="headerParams" />
</template>
<template v-slot:footer="footerParams">
<slot name="footer" v-bind="footerParams" />
</template>
<template v-slot:item="itemParams">
<slot name="item" v-bind="itemParams" />
</template>
<template v-slot:progress="progressParams">
<slot name="progress" v-bind="progressParams" />
</template>
<!-- put here the other slots you are interested in -->
</v-data-table>

expand slot on data table not working inside wrapper component

expand slot on data table not working inside wrapper component
I have a wrapper component around v-data-table like so:
<template>
<div>
<wrapper-data-table
:headers="tableHeaders"
:items="partLines"
:expand="expand"
>
<template v-slot:items="line">
<tr #click="line.expanded = !line.expanded">
<td>{{ line.item.binLocation }}</td>
<td>{{ line.item.reqQuantity }}</td>
</tr>
</template>
<template v-slot:expand="line">
<v-card flat>
<v-card-text>Peek-a-boo!</v-card-text>
</v-card>
</template>
</wrapper-data-table>
</div>
</template>
Wrapper Data-Table component
<template>
<div>
<div>
<v-data-table
v-model="selected"
v-bind.sync="tableProps"
:expand="expand"
item-key="name"
>
<!-- Pass on all slots -->
<slot v-for="slot in Object.keys($slots)" :name="slot" :slot="slot" />
<!-- Pass on all scoped slots -->
<template v-for="slot in Object.keys($scopedSlots)" :slot="slot" slot-scope="scope">
<slot :name="slot" v-bind="scope" />
</template>
</v-data-table>
</div>
</div>
</template>
I'm passing through all scoped slots which mostly works fine. However the expand slot doesn't seem to work. I want to expand the row on row click just like the example in the docs.
It works if I'm using the data table directly without the wrapper. however, when it's wrapped, there is no expansion. I can see that the 'expanded' prop is being changed though.

How to create a wrapper component in vuejs

I am trying to create a wrapper component around an existing component (q-table from the quasar-framework).
<!-- MyTable.vue -->
<template>
<q-table :title="title" ...>
<template v-slot:top="props">
...
</template>
<template v-slot:body="props">
<q-tr :props="props">
...
</q-tr>
</template>
</q-table>
</template>
<template v-slot:body="props"> and <template v-slot:top="props"> are slots of the q-table component.
How to overwrite these slots in the App.vue:
<!-- App.vue -->
<template>
<div>
<my-table>
???
</my-table>
</div>
</template>

Vue bypass html into sub-sub component

Lets say, I have the following vuejs structure:
<!-- MainComponent.vue -->
<template>
<childcomponent></childcomponent>
</template>
<!-- ChildComponent.vue -->
<template>
<div>
<grandchild></grandchild>
</div>
</template>
<!-- GrandchildComponent.vue -->
<template>
<div>
FooBar
<slot name="actions"></slot>
</div>
</template>
If I want to set a value for the grandchild-slot "actions" in the child-component, I have to do this:
<!-- ChildComponent.vue -->
<template>
<div>
<grandchild>
<template slot="actions">Actions from ChildComponent</template>
</grandchild>
</div>
</template>
But how to bypass "actions" from the MainComponent? I tried this, but thats not working:
<!-- MainComponent.vue -->
<template>
<childcomponent>
<template slot="actions" slot-scope="props">
Actions from MainComponent
</template>
</childcomponent>
</template>
<!-- ChildComponent.vue -->
<template>
<div>
<grandchild>
<slot name="actions">
<template slot="actions" slot-scope="props">
Actions from ChildComponent
</template>
</slot>
</grandchild>
</div>
</template>
After a lot of research, I think the only possible way of doing it, is only over the render function (https://v2.vuejs.org/v2/guide/render-function.html).
The main idea behind it is to write instead of html templates for ChildComponent.vue and GrandchildComponent.vue custom render functions.
A complete tutorial for a similar usecase can be found here:
https://github.com/ratiw/vuetable-2-tutorial/wiki