Reputation:
Of course, UI components are already wrapped enough conveniently. But, I want to reuse some components with custom options.
Especially, I want to reuse data tables component.
If all views have completely same header and data, it's no problem. When each views have different data, it's not working.
Here's my code:
Wrapper.vue
<template>
<v-card>
<v-card-title>
<span class="pr-3">{{ tableTitle }}</span>
<slot name="actions"/>
<v-spacer/>
<v-text-field
append-icon="search"
label="search"
single-line
hide-details
v-model="search"
/>
</v-card-title>
<v-data-table
:search="search"
:headers="headers"
:items="items"
hide-actions
>
<!-- problem is here! -->
<slot name="items" slot="items" slot-scope="props"></slot>
<template slot="expand" slot-scope="props">
<v-card flat>
<v-card-text>{{ props.item.note }}</v-card-text>
</v-card>
</template>
<template slot="no-data">
<v-alert :value="true" color="error" icon="warning">
no data.
</v-alert>
</template>
<template slot="no-results">
<v-alert :value="true" color="error" icon="warning">
no result.
</v-alert>
</template>
</v-data-table>
</v-card>
</template>
<script>
export default {
props: {
tableTitle: {type: String},
search: {type: String},
headers: {type: Array},
items: {type: Array}
}
}
</script>
Main.vue
<template>
<v-layout fluid fill-height justify-center align-center row wrap>
<v-flex sm12 md12 fill-height>
<main-custom-table
tableTitle="table1"
:headers="headers"
:items="items"
>
<template slot="actions">
<v-btn color="info">
<v-icon>add</v-icon>
add
</v-btn>
</template>
<!-- problem is here! -->
<template slot="items">
<tr>
<td class="text-xs-left">{{ items.id }}</td>
<td class="text-xs-left">{{ items.data1 }}</td>
<td class="text-xs-left">{{ items.data2 }}</td>
<td class="justify-center">
<v-btn icon class="mx-0" @click="">
<v-icon color="teal">edit</v-icon>
</v-btn>
</td>
</tr>
</template>
</main-custom-table>
</v-flex>
</v-layout>
</template>
<script>
export default {
name: "main",
data() {
return {
dialog: false,
search: '',
headers: [
{text: 'ID', value: 'id'},
{text: 'DATA1', value: 'data1'},
{text: 'DATA2', value: 'data2'}
],
items: [
{
'id': 1,
'data1': 10,
'data2': 12,
'note': aaaaaa
},
{
'id': 2,
'data1': 20,
'data2': 13,
'note': bbbbbb
},
{
'id': 5,
'data1': 30,
'data2': 14,
'note': cccccc
}
]
};
}
}
</script>
I want to write only tbody in Main.vue (and other views), and other optional elements in the Wrapper.vue.
Upvotes: 9
Views: 7798
Reputation: 121
It is not working because usage of your slot scope is not proper. Below change should work.
Wrapper.vue
<v-data-table
:search="search"
:headers="headers"
:items="items"
hide-actions
>
<!-- problem is here! -->
<slot name="items" v-bind:item="props" slot-scope="props"></slot>
<template slot="expand" slot-scope="props">
<v-card flat>
<v-card-text>{{ props.item.note }}</v-card-text>
</v-card>
</template>
<template slot="no-data">
<v-alert :value="true" color="error" icon="warning">
no data.
</v-alert>
</template>
<template slot="no-results">
<v-alert :value="true" color="error" icon="warning">
no result.
</v-alert>
</template>
</v-data-table>
Main.vue
<template slot="items" slot-scope="item">
<tr>
<td class="text-xs-left">{{ item.id }}</td>
<td class="text-xs-left">{{ item.data1 }}</td>
<td class="text-xs-left">{{ item.data2 }}</td>
<td class="justify-center">
<v-btn icon class="mx-0" @click="">
<v-icon color="teal">edit</v-icon>
</v-btn>
</td>
</tr>
</template>
For more information refer: https://v2.vuejs.org/v2/guide/components-slots.html#Scoped-Slots-with-the-slot-scope-Attribute
Upvotes: 0
Reputation: 610
In order to wrap a component you should use the following syntax.
inheritAttrs: false
and v-bind:$attrs
: Use that option on the component to pass all attributes used in the wrapper to the target component.v-for="(index, name) in $scopedSlots" v-slot:[name]="data"
and <slot :name="name" v-bind="data"></slot>
: Use that to iterate over all slots defined on the target component, and define them in the wrapper either.v-on:$listeners
: Use that option to pass all events coming from the target component throug the wrapper.After defining all that, the wrapper will work as desired. It'll just wrap the component. Then you can add your customizations.
Wrapper
<template>
<v-data-table
v-bind="$attrs"
v-on="$listeners"
>
<template v-for="(index, name) in $scopedSlots" v-slot:[name]="data">
<slot :name="name" v-bind="data"></slot>
</template>
</v-data-table>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
@Component({
name: 'BaseTable',
inheritAttrs: false
})
export default class extends Vue {
}
</script>
Upvotes: 2