BootstrapVue table-template inside Vue slot

11,505

Solution 1

just build your custom component and pass any custom props you need some thing like this:

<template>
  <b-table v-bind="$attrs" v-on="$listeners" custom-prop="any">
    <slot v-for="(_, name) in $slots" :name="name" :slot="name" />
    <template
      v-for="(_, name) in $scopedSlots"
      :slot="name"
      slot-scope="slotData"
      ><slot :name="name" v-bind="slotData"
    /></template>
  </b-table>
</template>

<script>
export default {
  name: 'AppTable',
}
</script>

<style scoped></style>


this works like a charm!

Solution 2

You can use a component's render function to pass slots and scopedSlots to another component (like b-table). But then you don't get to use a template. To let you have a template (with pagination, searching, etc.) you can wrap the rendering component into another component that does have a template. So then you would have a custom-table component which contains pagination and a table-wrapper component, and the table-wrapper component would render a b-table.

Here's a very specific example..

const constItems = [{
    index: 0,
    isActive: true,
    age: 40,
    first_name: 'Dickerson',
    last_name: 'Macdonald'
  },
  {
    index: 1,
    isActive: false,
    age: 21,
    first_name: 'Larsen',
    last_name: 'Shaw'
  },
  {
    index: 2,
    isActive: false,
    age: 89,
    first_name: 'Geneva',
    last_name: 'Wilson'
  },
  {
    index: 3,
    isActive: true,
    age: 38,
    first_name: 'Jami',
    last_name: 'Carney'
  }
];

const bTableProps = {
  items: {
    type: [Array, Function],
    default: undefined
  },
  fields: {
    type: [Object, Array],
    default: undefined
  }
};

const constFields = [
  'index',
  'isActive',
  'age',
  'first_name',
  'last_name'
];

Vue.component('table-wrapper', {
  props: Object.assign({}, bTableProps),
  render(h) {
    return h('b-table', {
      props: this.$props,
      slots: this.$parent.$slots,
      scopedSlots: this.$parent.$scopedSlots,
      on: {
        'row-clicked': (item, index, event) => alert('clicked ' + index)
      }
    });
  }
});

Vue.component('custom-table', {
  template: '<div><h3>hello table</h3><table-wrapper :items="items" :fields="fields"></table-wrapper></div>',
  props: Object.assign({}, bTableProps)
});

new Vue({
  el: "#app",
  data: {
    items: constItems,
    fields: constFields
  }
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<link href="https://unpkg.com/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://unpkg.com/[email protected]/dist/bootstrap-vue.css" rel="stylesheet" />
<script src="https://unpkg.com/[email protected]/dist/polyfill.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/bootstrap-vue.js"></script>

<div id="app">
  <custom-table :items="items" :fields="fields">
    <template slot="index" slot-scope="data">
			{{ data.index + 1 }}
		</template>
  </custom-table>
  <custom-table :items="items" :fields="fields">
    <template slot="index" slot-scope="data">
			{{ data.index + 2 }}
		</template>
  </custom-table>
  <custom-table :items="items" :fields="fields">
    <template slot="index" slot-scope="data">
			{{ data.index + 3 }}
		</template>
  </custom-table>
</div>
Share:
11,505
Decay42
Author by

Decay42

Computer science student from Germany. Mostly interested in Web Development (HTML5, CSS, JS, jQuery, PHP).

Updated on June 04, 2022

Comments

  • Decay42
    Decay42 almost 2 years

    I want to create a parent component for a bootstrap-vue table with custom data rendering (templates).

    Right now, this kinda looks like this:

    <b-table
         :items="result"
         :fields="fields"
         :current-page="currentPage"
         :per-page="perPage">
        <template slot="Index" slot-scope="data">
            {{ data.index + 1 }}
        </template>
        <!-- more templates for various columns here -->
    </b-table>
    <b-pagination 
         align="center" 
         :total-rows="result.length" 
         v-model="currentPage" 
         :per-page="perPage"
    />
    

    The reason I want to wrap this in a component is because I use this table layout, including the pagination and all of its attributes (like striped, bordered, etc.) multiple times.

    The only thing that changes, are the column-templates.

    I know, the Vue way to do that would be to create a slot like <slot name="x"></slot> and fill it with <template slot="x">...</template>. For one thing, that would coincide with the bootstrap-vue template and on the other hand, bootstrap-vue only seems to render the templates correctly, if they are placed right inside b-table.

    Basically, what I want to achieve is a component like this:

    <b-table>
        <slot name="templates"/>
    </b-table>
    <b-pagination stuff.../>
    

    And use it in a child component like this:

    <TemplateTable>
        <template slot="templates">
            <template slot="Index" slot-scope="data">
                {{ data.index + 1 }}
            </template>
            <!-- more templates -->
        </template>
    </TableTemplate>
    

    Has anyone done something like this and figured out a way to solve it?

  • Stan Smulders
    Stan Smulders over 4 years
    Thanks for sharing this! It had me in a complete bind trying to figure it out. I'll post my .vue solution as well for future reference :)
  • nardnob
    nardnob over 4 years
    Glad it could help. I posted it as much for my own future reference as for everyone else =)