[Vuejs]-Sorting indicators do not appear in customized VDataTable with Vue 3 and Vuetify 3

2👍

The sorting indicators are part of the default column v-slot. When you use your own v-slot, you erase everything that was there by default. If you customize the column header, you will need to re-implement the sorting arrows manually.

From what I can gather from the v-data-table API documentation and inspecting the CSS of normal columns, the v-slot code below will recreate the default column header with working sorting icons for a column named "Price"

<template v-slot:column.price="{ column, getSortIcon, toggleSort }">
  <div class="v-data-table-header__content">
    <span>Price</span>
    <v-icon
      class="v-data-table-header__sort-icon"
      :icon="getSortIcon(column)"
      @click="toggleSort(column)"
    />
  </div>
</template>

Use this as your starting v-slot code for your different columns and customize however you see fit.

Vuetify Playground example (reproducing default "Calories" column)


Update #1

Example of updating your first column…

Change this:

<template v-slot:column.price="{ header }">
  <!-- {{ header.text.toUpperCase() }} -->
  Price ({{wallets.rel.ticker }}) 
</template>

To this:

<template v-slot:column.price="{ column, getSortIcon, toggleSort }">
  <div class="v-data-table-header__content">
    <span>Price ({{wallets.rel.ticker }}) </span>
    <v-icon
      class="v-data-table-header__sort-icon"
      :icon="getSortIcon(column)"
      @click="toggleSort(column)"
    />
  </div>
</template>

Notice for this column you only have to update what is in the <span>. For other columns you’ll need to also update the name in v-slot:column.price with the correct column name.


Update #2

It seems the only way I can possibly make sense is if I write the entirety of the template code for you to copy/paste into your app, so here you go:

<template>
  <v-card class="mx-auto" outlined>
    <v-toolbar flat dense>
      <v-toolbar-title>
        <span class="subheading">AtomicDEX order book</span>
      </v-toolbar-title>
      <div class="flex-grow-1"></div>
      <v-chip
        class="ma-2"
        outlined
        @click="refreshMarket()"
      >
        <v-icon left>mdi-server-plus</v-icon>Refresh
      </v-chip>
    </v-toolbar>
    <div v-if="marketdata.asks">
      <div>
        <v-layout>
          <v-flex md lg>
            <v-card-title>Asks</v-card-title>
            <div class="table-container">
            <v-data-table
              dense
              :sort-by="sortBy"
              :headers="asksHeaders"
              :items="marketdata.asks"
              :rows-per-page="-1"
              options="disablePagination"
              class="elevation-1"
            >
              <template v-slot:column.price="{ column, getSortIcon, toggleSort }">
                <div class="v-data-table-header__content">
                  <span>Price ({{wallets.rel.ticker }}) </span>
                  <v-icon
                    class="v-data-table-header__sort-icon"
                    :icon="getSortIcon(column)"
                    @click="toggleSort(column)"
                  />
                </div>
              </template>

              <template v-slot:column.maxvolume="{ column, getSortIcon, toggleSort }">
                <div class="v-data-table-header__content">
                  <span>Amount ({{wallets.base.ticker }})</span>
                  <v-icon
                    class="v-data-table-header__sort-icon"
                    :icon="getSortIcon(column)"
                    @click="toggleSort(column)"
                  />
                </div>
              </template>

              <template v-slot:column.relamount="{ column, getSortIcon, toggleSort }">
                <div class="v-data-table-header__content">
                  <span>Total ({{wallets.rel.ticker }})</span>
                  <v-icon
                    class="v-data-table-header__sort-icon"
                    :icon="getSortIcon(column)"
                    @click="toggleSort(column)"
                  />
                </div>
              </template>

              <!-- Rounding from https://www.jacklmoore.com/notes/rounding-in-javascript/ -->
              <!-- Better to move to computed function for maintainability/non-repetitive -->
              <template v-slot:item.price="{ item }">
                {{ roundedPrice(item.columns.price) }}
              </template>

              <template v-slot:item.price2="{ item }">
                {{ roundedPrice(item.columns.price) }}
  <!-- Remaining code -->

<!--
better implementation handled in parent component on load of orders, then promise to set flag
                <v-chip v-if="hasMyOrder(item.price)" color="purple" dark>me</v-chip>
-->
                <v-chip v-if="item.myOrder" x-small color="purple" dark>*</v-chip>
              </template> 

              <template v-slot:item.maxvolume="{ item }">
                {{ roundedPrice(item.columns.maxvolume) }}
              </template>

              <template v-slot:item.relamount="{ item }">
                {{ roundedPrice(item.columns.price * item.columns.maxvolume) }}
              </template>
              <template v-slot:bottom></template>
            </v-data-table>
            </div>
          </v-flex>
        </v-layout>
      </div>
    </div>
    <div v-else>No current asks to display.</div>
    <h2 class="pl-3">{{ middlePriceSpreadData.middle }} Middle Price (Spread: {{ middlePriceSpreadData.spread}} %)</h2>

    <div v-if="marketdata.bids">
      <div>
        <v-layout>
          <v-flex md lg>
            <v-card-title>Bids</v-card-title>
            <div class="table-container">
            <v-data-table
               dense
              :sort-by="sortBy"
              :disable-pagination="true"
              :headers="bidsHeaders"
              :items="marketdata.bids"
              :items-per-page="-1"
            >
              <template v-slot:column.price="{ column, getSortIcon, toggleSort }">
                <div class="v-data-table-header__content">
                  <span>Price ({{wallets.rel.ticker }}) </span>
                  <v-icon
                    class="v-data-table-header__sort-icon"
                    :icon="getSortIcon(column)"
                    @click="toggleSort(column)"
                  />
                </div>
              </template>
              
              <template v-slot:column.baseamount="{ column, getSortIcon, toggleSort }">
                <div class="v-data-table-header__content">
                  <span>Amount ({{wallets.base.ticker }})</span>
                  <v-icon
                    class="v-data-table-header__sort-icon"
                    :icon="getSortIcon(column)"
                    @click="toggleSort(column)"
                  />
                </div>
              </template>

              <template v-slot:column.maxvolume="{ column, getSortIcon, toggleSort }">
                <div class="v-data-table-header__content">
                  <span>Total ({{wallets.rel.ticker }})</span>
                  <v-icon
                    class="v-data-table-header__sort-icon"
                    :icon="getSortIcon(column)"
                    @click="toggleSort(column)"
                  />
                </div>
              </template>

              <!-- Rounding from https://www.jacklmoore.com/notes/rounding-in-javascript/ -->
              <!-- Better to move to computed function for maintainability/non-repetitive -->
              <template v-slot:item.price="{ item }">
                  {{ roundedPrice(item.columns.price) }}
              </template>
              
              <!-- For highlighting my orders, TODO need a price:uuid array before grouping by price in AppTraderview   -->
              <template v-slot:item.price2="{ item }">
                  {{ roundedPrice(item.columns.price) }}
<!--
better implementation in parent component
                <v-chip v-if="hasMyOrder(item.price)" color="purple" dark>me</v-chip>
-->
                <v-chip v-if="item.myOrder" x-small dark>*</v-chip>
              </template> 
              <template
                v-slot:item.baseamount="{ item }"
              >{{ roundedPrice(item.columns.maxvolume / item.columns.price) }}</template>
              <template
                v-slot:item.maxvolume="{ item }"
              >{{ roundedPrice(item.columns.maxvolume) }}</template>
              <template v-slot:bottom></template>
            </v-data-table>
            </div>
          </v-flex>
        </v-layout>
        
      </div>
    </div>
    <div v-else>No current bids to display.</div>
  </v-card>
</template>

Update #3

here is your v-data-table, isolated from the rest of your code, with working sort icons.

👤yoduh

Leave a comment