【问题标题】:Pass component as prop in Vue JS在 Vue JS 中将组件作为道具传递
【发布时间】:2022-01-25 03:19:38
【问题描述】:

简介:我正在探索 Vue Js,但在尝试制作 动态数据表 组件时遇到了困难我面临的问题是我无法通过道具传递组件并将其呈现在表格中。

问题:所以基本上我要做的是从 v-data-table 中的 headers 属性中传递一些自定义组件,例如如:

headers = [
    { text: 'Name', value: 'name' },
    {
      text: 'Phone Number',
      value: 'phone_number',
      render: () => (
         <div>
           <p>Custom Render</p>
         </div>
     )
    },
    { text: 'Actions', value: 'actions' }
]

所以从上面的代码我们可以看出我想从Phone Number header里面的render函数渲染那个段落,我之前在React Js中做过这个事情,但是如果有人能指出我正确的方向,我无法在 Vue Js 中找到一种方法,那就太好了。提前谢谢你。

【问题讨论】:

标签: vue.js vuejs2 vue-component vuetify.js


【解决方案1】:

好的,我不认为这是最好的方法,但它适用于我,也许它适用于其他人。

我所做的是像这样修改了 headers 数组:

 headers = [
    { text: 'Name', align: 'start', sortable: false, value: 'name' },
    {
      text: 'Phone Number',
      key: 'phone_number',
      value: 'custom_render',
      render: Vue.component('phone_number', {
        props: ['item'],
        template: '<v-chip>{{item}}</v-chip>'
      })
    },
    { text: 'Bookings', value: 'bookings_count' },
    {
      text: 'Company',
      key: 'company.name',
      value: 'custom_render',
      render: Vue.component('company_name', {
        props: ['item'],
        template:
          '<v-chip color="pink darken-4" text-color="white">{{item}}</v-chip>'
      })
    },
    { text: 'Actions', value: 'actions', sortable: false }
  ]

v-data-table 中,我引用了 custom_render 的插槽,并像这样渲染那个 component

 <template v-slot:[`item.custom_render`]="{ item, header }">
      <component
        :is="header.render"
        :item="getValue(item, header.key)"
      ></component>
    </template>

要进入像 company.name 这样的嵌套对象,我创建了一个名为 getValue 的函数,它接受 2 个参数,即我们需要的对象和指向该值的路径它作为键存储在 headers 数组中(例如 company.name)并使用 loadash 返回值。

getValue函数:

 getValue (item: any, path: string): any {
    return loadash.get(item, path)
  }

注意:这只是最初的想法,对我有用。如果有人有更好的想法,请参与这篇文章。看看我传递给那些动态组件的 props,注意你可以通过这种方式传递更多的变量。

【讨论】:

    【解决方案2】:

    您有 2 个选项 - 插槽和动态组件。

    让我们首先探索插槽:

    <template>
      <v-data-table :items="dataItems" :headers="headerItems">
        <template slot="item.phone_number" slot-scope="{item}">
          <v-chip>{{ item.phone_number }}</v-chip>
        </template>
        <template slot="item.company_name" slot-scope="{item}">
          <v-chip color="pink darken-4" text-color="white">{{ item.company_name }}</v-chip>
        </template>
      </v-data-table>
    </template>
    

    数据表为您提供了可以自定义内容的位置。如果您想让您的组件更可重用并希望从您的父组件填充这些插槽 - 那么您需要将这些插槽重新公开给父组件:

    <template>
      <v-data-table :items="dataItems" :headers="headerItems">
        <template slot="item.phone_number" slot-scope="props">
          <slot name="phone" :props="props" />
        </template>
        <template slot="item.company_name" slot-scope="props">
          <slot name="company" :props="props" />
        </template>
      </v-data-table>
    </template>
    

    如果您不知道要自定义哪些槽 - 您可以重新公开所有数据表槽:

    <template>
      <v-data-table
        :headers="headers"
        :items="items"
        :search="search"
        hide-default-footer
        :options.sync="pagination"
        :expanded="expanded"
        class="tbl_manage_students"
        height="100%"
        fixed-header
        v-bind="$attrs"
        @update:expanded="$emit('update:expanded', $event)"
      >
        <!-- https://devinduct.com/blogpost/59/vue-tricks-passing-slots-to-child-components -->
        <template v-for="(index, name) in $slots" v-slot:[name]>
          <slot :name="name" />
        </template>
        <template v-for="(index, name) in $scopedSlots" v-slot:[name]="data">
          <slot :name="name" v-bind="data" />
        </template>
    
        <v-alert slot="no-results" color="error" icon="warning">
          {{ $t("no_results", {term: search}) }}"
        </v-alert>
        <template #footer="data">
          <!-- you can safely skip the "footer" slot override here - so it will be passed through to the parent component -->
          <table-footer :info="data" @size="pagination.itemsPerPage = $event" @page="pagination.page = $event" />
        </template>
      </v-data-table>
    </template>
    
    <script>
    import tableFooter from '@/components/ui/TableFooter'; // you can safely ignore this component in your own implementation
    
    export default
    {
      name: 'TeacherTable',
      components:
        {
          tableFooter,
        },
      props:
        {
          search:
            {
              type: String,
              default: ''
            },
          items:
            {
              type: Array,
              default: () => []
            },
          sort:
            {
              type: String,
              default: ''
            },
          headers:
            {
              type: Array,
              required: true
            },
          expanded:
            {
              type: Array,
              default: () => []
            }
        },
      data()
      {
        return {
          pagination:
            {
              sortDesc: [false],
              sortBy: [this.sort],
              itemsPerPageOptions: [25, 50, 100],
              itemsPerPage: 25,
              page: 1,
            },
        };
      },
      watch:
        {
          items()
          {
            this.pagination.page = 1;
          },
          sort()
          {
            this.pagination.sortBy = [this.sort];
            this.pagination.sortDesc = [false];
          },
        }
    };
    </script>
    

    动态组件可以通过 props 提供:

    <template>
      <v-data-table :items="dataItems" :headers="headerItems">
        <template slot="item.phone_number" slot-scope="{item}">
          <component :is="compPhone" :phone="item.phone_number" />
        </template>
        <template slot="item.company_name" slot-scope="{item}">
          <component :is="compCompany" :company="item.company_name" />
        </template>
      </v-data-table>
    </template>
    
    <script>
    export default
    {
      name: 'MyTable',
      props:
      {
        compPhone:
        {
          type: [Object, String], // keep in mind that String type allows you to specify only the HTML tag - but not its contents
          default: 'span'
        },
        compCompany:
        {
          type: [Object, String],
          default: 'span'
        },
      }
    }
    </script>
    

    槽比动态组件更强大,因为它们(槽)使用依赖倒置原则。你可以在Markus Oberlehner's blog阅读更多内容

    【讨论】:

    • 我没有尝试您的解决方案,但我敢打赌它会起作用,感谢您向我解释一切。您是否在我的实现中发现了一些错误,您可以指出,所以我会知道我是否需要更改我的代码并使其与您的相同,因为目前我的实现在我的情况下工作。谢谢你:)
    猜你喜欢
    • 2021-04-25
    • 2021-02-24
    • 1970-01-01
    • 1970-01-01
    • 2018-05-29
    • 2020-01-08
    • 2020-12-04
    • 1970-01-01
    • 2018-09-23
    相关资源
    最近更新 更多