Vue 2.5-插槽/插槽范围-将库组件封装到自定义组件中

时间:2018-08-15 19:26:58

标签: vuejs2

我正在使用vue 2.5和库Bootstrap-vue。 我对该库的表组件感兴趣:https://bootstrap-vue.js.org/docs/components/table

但是我想封装该组件,以使用不需要重复的自定义配置来创建自己的组件。这样,我不必每次都管理分页和过滤,并且可以添加其他功能,例如数据导出。

所以我创建了一个表格帮助器组件(目前仅处理分页)

<template>
    <div>
        <b-table striped hover responsive
                 :items="items" :fields="fields"
                 :current-page="currentPage" per-page="10">
            <slot></slot>
        </b-table>
        <b-pagination :total-rows="items.length" per-page="10" v-model="currentPage"></b-pagination>
    </div>
</template>

<script>
    import bTable from 'bootstrap-vue/es/components/table/table'
    import bPagination from 'bootstrap-vue/es/components/pagination/pagination'

    export default {
        name: "table-helper",
        props: ['items', 'fields'],
        data() {
            return {
                currentPage: 1,
            }
        },
        components: {
            'b-table': bTable,
            'b-pagination': bPagination
        }
    }
</script>

我想像这样使用我的组件(使用bootstrap-vue插槽的可能性来重新格式化列):

<table-helper :items="users" :fields="fields">
    <template slot="fullName" slot-scope="data">
        {{data.item.first_name}} {{data.item.last_name}}
    </template>
</table-helper>

显然,它不起作用(我得到了表格,但没有格式化的列),因为<template slot="fullName" slot-scope="data">是指我的自定义组件,而不是b表组件。

所以我想知道一种封装库组件的方法,该组件使用这样的插槽和插槽范围。

谢谢您的帮助。

1 个答案:

答案 0 :(得分:0)

要点:

  1. 模板与JSX :您可能必须使用JSX来实现它,尤其是对于广告位

  2. 插槽:对于您的情况,父组件的插槽将是b-table的子VNode,因此将插槽重塑为一个数组并将上下文从父级更改为当前(如果未正确显示 scopedSlot 。),则将它们放入function = h的第三个参数中(或者您可以调用createElement)。有关详细信息,请检查Vue Guide: Create Element Arguments

无论如何,请仔细阅读Best Way To Implement HOC,然后您就可以实现目标。

Vue.config.productionTip = false

Vue.component('table-helper', {
  render (h) {
    const slots = Object.keys(this.$slots)
      .reduce((arr, key) => arr.concat(this.$slots[key]), [])
      .map(vnode => {
        vnode.context = this._self
        return vnode
      })
    const self = this
    
    return h('div', [
      h('b-table', {
        on: self.$listeners,
        props: Object.assign(self.$props, {currentPage: self.currentPage}),
        scopedSlots: self.$scopedSlots,
        attrs: self.$attrs
      },slots),
      h('b-pagination', {
        props: self.$props,
        domProps: {
          value: self.currentPage
        },
        on: {
          input: function (event) {
            self.currentPage = event
          }
        }
      })
    ])
  },
  //mixins: [{bTable.props}, {bPagination.props}],
  props: ['items', 'fields', 'perPage','totaRows'],
  data() {
    return {
      currentPage: 1,
    }
  }
})

new Vue({
  el: '#app',
  data() {
    return {
      fields: [ 'first_name', 'last_name', 'age', 'fullName' ],
      users: [
        { isActive: true, age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
        { isActive: false, age: 21, first_name: 'Larsen', last_name: 'Shaw' },
        { isActive: false, age: 89, first_name: 'Geneva', last_name: 'Wilson' },
        { isActive: true, age: 38, first_name: 'Jami', last_name: 'Carney' },
        { isActive: true, age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
        { isActive: false, age: 21, first_name: 'Larsen', last_name: 'Shaw' },
        { isActive: false, age: 89, first_name: 'Geneva', last_name: 'Wilson' },
        { isActive: true, age: 38, first_name: 'Jami', last_name: 'Carney' },
        { isActive: true, age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
        { isActive: false, age: 21, first_name: 'Larsen', last_name: 'Shaw' },
        { isActive: false, age: 89, first_name: 'Geneva', last_name: 'Wilson' },
        { isActive: true, age: 38, first_name: 'Jami', last_name: 'Carney' }
      ]
    }
  }
})
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<script src="//unpkg.com/babel-polyfill@latest/dist/polyfill.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue/dist/bootstrap-vue.js"></script>


<div id="app">
  <table-helper :items="users" :fields="fields" :per-page="10" :total-rows="users.length">
      <template slot="fullName" slot-scope="data">
          {{data.item.first_name}} {{data.item.last_name}}
      </template>
  </table-helper>
</div>