1

I am working on a Vuetify 3 data tabe and need to implement a "View More" button for description that exceed three lines. I want to use scrollHeight and clientHeight to determine if the text is truncated and provide an option to expand it.

Vuetify Playground: Playground Link

Here is a simplified version of my implementation:

<template>
  <v-data-table :headers="headers" :items="items">
    <template #item.description="{ item }">
      <div>
        <div
          ref="descriptionRef"
          class="three_lines_description"
          :style="{ overflow: isExpanded[item.name] ? 'visible' : 'hidden' }"
        >
          {{ item.description }}
        </div>
        <v-btn
          v-if="isTruncated[item.name]"
          @click="toggleExpand(item.name)"
          text
        >
          {{ isExpanded[item.name] ? 'View Less' : 'View More' }}
        </v-btn>
      </div>
    </template>
  </v-data-table>
</template>

<script>
import { ref, onMounted } from 'vue';

export default {
  data() {
    return {
      headers: [
        { title: "Name", key: "name" },
        { title: "Email", key: "email" },
        { title: "Description", key: "description" },
      ],
      items: [
        // Sample items...
      ],
      isExpanded: {},
      isTruncated: {},
    };
  },
  methods: {
    toggleExpand(name) {
      this.isExpanded[name] = !this.isExpanded[name];
    },
    checkTruncation() {
      // Logic to check if the text is truncated...
    }
  },
  mounted() {
    this.checkTruncation();
  }
};
</script>

<style scoped>
.three_lines_description {
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
}
</style>

Issue

  • The "View More" button does not appear for descriptions that exceed three lines.
  • I am unsure how to properly use refs in v-data-table to determine if the text is truncated.

This is the simplified design of my code now which changes the format for the description column only. What is the correct approach to implement this "View More" functionality in a Vuetify data table?

If the solution works, I will accept it as answer and give an upvote.

Pagination not working

1 Answer 1

1

This is a Vue.js 3 possible solution:

<script setup lang="ts">
  import { ref, onMounted, nextTick } from "vue";

  const headers = [
    { title: 'Name', key: 'name' },
    { title: 'Email', key: 'email' },
    { title: 'Description', key: 'description' },
  ];

  const items = ref([
    {
      name: 'John Rambo',
      email: '[email protected]',
      description: 'This a very short description.',
      showButton: false,
      showButtonText: "View More",
    },
    {
      name: 'John Doe',
      email: '[email protected]',
      description: 'This a short test description for the sake of testing.',
      showButton: false,
      showButtonText: "View More",
    },
    {
      name: 'Jane Doe',
      email: '[email protected]',
      description:
        'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi et nunc id augue imperdiet tempor. Duis leo nisl, aliquet et dapibus ut, eleifend sed nunc. Aenean vulputate purus tellus, ac rutrum quam mattis ac. Quisque et nunc hendrerit, pretium quam eget, dictum mauris. Aliquam pulvinar velit ac tellus porttitor, quis sagittis diam consequat. Nulla facilisi. Morbi a tempor ex. Integer tempus est ipsum, id pretium magna maximus at. Praesent finibus tempus tempus. Proin fringilla imperdiet odio eget semper. Maecenas tempus ipsum sit amet luctus tincidunt. Mauris consectetur risus in lacus placerat congue. Quisque quis arcu ut ligula finibus dapibus eu sit amet nisl. Maecenas a mi rhoncus, bibendum erat vel, pretium arcu. Vestibulum eu lectus laoreet, efficitur quam sed, ultricies risus. Morbi pharetra mauris vel venenatis lacinia.',
      showButton: false,
      showButtonText: "View More",
    },
    {
      name: 'Random Guy',
      email: '[email protected]',
      description:
        'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi et nunc id augue imperdiet tempor. Duis leo nisl, aliquet et dapibus ut, eleifend sed nunc. Aenean vulputate purus tellus, ac rutrum quam mattis ac. Quisque et nunc hendrerit, pretium quam eget, dictum mauris. Aliquam pulvinar velit ac tellus porttitor, quis sagittis diam consequat. Nulla facilisi. Morbi a tempor ex. Integer tempus est ipsum, id pretium magna maximus at. Praesent finibus tempus tempus. Proin fringilla imperdiet odio eget semper. Maecenas tempus ipsum sit amet luctus tincidunt. Mauris consectetur risus in lacus placerat congue. Quisque quis arcu ut ligula finibus dapibus eu sit amet nisl. Maecenas a mi rhoncus, bibendum erat vel, pretium arcu. Vestibulum eu lectus laoreet, efficitur quam sed, ultricies risus. Morbi pharetra mauris vel venenatis lacinia.',
      showButton: false,
      showButtonText: "View More",
    },
  ]);
  const descriptionRefs = ref([]);

  function increaseSize(index: number) {
    const divElement: HTMLDivElement = descriptionRefs.value[index];

    if (divElement.dataset.expanded === "true") {
      nextTick(() => {
        divElement.dataset.expanded = "false"
        divElement.classList.add('three_lines_description');
        items.value[index].showButtonText = "View More";
      })
      return;
    }

    nextTick(() => {
      divElement.dataset.expanded = "true"
      divElement.dataset.showButton = "true"
      divElement.classList.remove("three_lines_description");
      items.value[index].showButtonText = "Show less";
    });
  }

  function checkIfNeedsButton() {
    descriptionRefs.value.forEach((item: HTMLDivElement, index) => {
      if (item.scrollHeight > item.clientHeight) {
        items.value[index].showButton = true;
      }
    });
  }

  onMounted(() => {
    nextTick(() => {
      checkIfNeedsButton();
    })
  });
</script>

<template>
  <v-data-table :headers="headers" :items="items">
    <template #item.description="{ item, index }">
      <div>
        <div
          :ref="(el) => {
            descriptionRefs.push(el);
          }"
          class="three_lines_description px-1 py-1"
        >
          {{ item.description }}
        </div>
        <v-btn
          v-if="item.showButton"
          class="mt-2 mb-2"
          @click="increaseSize(index)"
        >
          {{ item.showButtonText }}
        </v-btn>
      </div>
    </template>
  </v-data-table>
</template>

<style scoped>
  .three_lines_description {
    overflow: hidden;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    line-clamp: 3;
  }
</style>

Using the following tools:

  • Function refs: To assign every description to a refs Array.
  • NextTick utility this is neccesary because: When you mutate reactive state in Vue, the resulting DOM updates are not applied synchronously.
  • Reactivity: To mutate items and descriptionRefs when needed

Vuetify Playground: example

PD: Also there is a expandable Row feature in Vuetify

Sign up to request clarification or add additional context in comments.

6 Comments

Hey, thanks for the reply! I clicked the example in your Vuetify Playground but I do not see the "View More" button
Hi! No problem. don't know why but after you type something in the web IDE the code works. Really odd. Nevermind, I just hope this helps you to reach your goal!
Your solution works great with a little tweak needed! Basically, in checkIfNeedsButton, item is a reactive property but the DOM is still referencing the same reference of item even though we added key-value pairs to the object by item.value[index]...., so the DOM thinks that nothing has changed so nothing to be updated. The trick is to reassign item with the updated values to force the DOM to be updated with the new properties.
Excellent! I think when I wrote that final part I was a little asleep hehe. Hope it helps you, thanks for the upvote and happy coding!
Hey @Alejandro, I am facing another issue. If I have many rows on one page, the table will be paginated. When I click to the next page, it doesn't show "View More". I have attached the playground link on my post.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.