Files
oa/documentation/flex-table/v-flex-table-wrapper-documentation.md
2025-05-24 01:49:48 +09:00

4.9 KiB

VFlexTableWrapper

You can wrap <VFlexTable /> using <VFlexTableWrapper /> to render large amount of data. This will also enable sorting, searching and paginating through the data. Instead of providing data and columns properties to <VFlexTable />, you just have to declare them to <VFlexTableWrapper />.

In addition to the base columns settings, searchable, sortable attributes can be used to enable search and sorting. Of course, custom sort and filter function can be used. To easily customize your table, <VFlexPagination />, <VFlexTableToolbar> and <VFlexTableSortColumn> components can be used to display controls for pagination and search.

Check the markup for more details about usage.

<script setup lang="ts">
import type {
  VFlexTableWrapperSortFunction,
  VFlexTableWrapperFilterFunction,
} from '/@src/components/base/table/VFlexTableWrapper.vue'
import { users } from '/@src/data/layouts/card-grid-v1'

type User = (typeof users)[0]

// duplicate user data to grow the array
const data: User[] = []
for (let i = 0; i < 1000; i++) {
  data.push(...users)
}

// this is a sample for custom sort function
const locationSorter: VFlexTableWrapperSortFunction<User> = ({ order, a, b }) => {
  if (order === 'asc') {
    return a.location.localeCompare(b.location)
  }
  else if (order === 'desc') {
    return b.location.localeCompare(a.location)
  }

  return 0
}

// this is a sample for custom filter function
const userFilter: VFlexTableWrapperFilterFunction<User> = ({ searchTerm, row }) => {
  if (!searchTerm) {
    return true
  }

  // search either in the name or the bio
  return (
    row.name.toLocaleLowerCase().includes(searchTerm.toLocaleLowerCase())
    || row.bio.toLocaleLowerCase().includes(searchTerm.toLocaleLowerCase())
  )
}

const columns = {
  name: {
    label: 'Username',
    media: true,
    grow: true,
    searchable: true,
    sortable: true,
    filter: userFilter,
  },
  location: {
    label: 'Location',
    sortable: true,
    searchable: true,
    sort: locationSorter,
  },
  role: 'Role',
} as const
</script>

<template>
  <VFlexTableWrapper :columns="columns" :data="data">
    <!--
      Here we retrieve the internal wrapperState.
      Note that we can not destructure it
    -->
    <template #default="wrapperState">
      <!-- We can place any content inside the default slot-->
      <VFlexTableToolbar>
        <template #left>
          <!-- We can bind wrapperState.searchInput to any input -->
          <VField>
            <VControl icon="lucide:search">
              <input
                v-model="wrapperState.searchInput"
                type="text"
                class="input is-rounded"
                placeholder="Filter..."
              >
            </VControl>
          </VField>
        </template>

        <template #right>
          <!-- We can also bind wrapperState.limit -->
          <VField>
            <VControl>
              <div class="select is-rounded">
                <select v-model="wrapperState.limit">
                  <option :value="1">
                    1 results per page
                  </option>
                  <option :value="10">
                    10 results per page
                  </option>
                  <option :value="15">
                    15 results per page
                  </option>
                  <option :value="25">
                    25 results per page
                  </option>
                  <option :value="50">
                    50 results per page
                  </option>
                </select>
              </div>
            </VControl>
          </VField>
        </template>
      </VFlexTableToolbar>

      <!--
        The VFlexTable "data" and "columns" props
        will be inherited from parent VFlexTableWrapper
      -->
      <VFlexTable rounded>
        <!-- Custom "name" cell content -->
        <template #body-cell="{ row, column }">
          <template v-if="column.key === 'name'">
            <VAvatar
              size="medium"
              :picture="row.medias.avatar"
              :badge="row.medias.badge"
              :initials="row.initials"
            />
            <div>
              <span class="dark-text" :title="row.name">
                {{ row?.shortname }}
              </span>
              <VTextEllipsis
                width="280px"
                class="light-text"
                :title="row.bio"
              >
                <small>{{ row?.bio }}</small>
              </VTextEllipsis>
            </div>
          </template>
        </template>
      </VFlexTable>

      <!-- Table Pagination with wrapperState.page binded-->
      <VFlexPagination
        v-model:current-page="wrapperState.page"
        class="mt-6"
        :item-per-page="wrapperState.limit"
        :total-items="wrapperState.total"
        :max-links-displayed="5"
        no-router
      />
    </template>
  </VFlexTableWrapper>
</template>