Home > OS >  How to hide q-expansion-item header content when clicked?
How to hide q-expansion-item header content when clicked?

Time:07-02

I am trying to create an interface for a support ticket system in my Vue 3 / Quasar / TypeScript app.

It consists of tickets and message threads.

When viewing a single ticket I want to display each thread of the ticket as a q-expansion-item. The expansion item shows a preview of the thread, and when clicked the preview disappears and the full message thread is shown.

I have a working component for this. The problem is that all thread previews disappear when opening a q-expansion-item. But I only want the thread preview of the q-expansion-item that was clicked to disappear.

Any idea how I can fix this?

This is what it looks like before opening:

enter image description here

This is what it looks like after opening. Note that ALL thread previews have gone, but I only want the preview for the top thread that was clicked to disappear:

enter image description here

I tried to create a minimal reproduction in codepen.io and codesandbox.io but I couldn't get it working with Vue 3 / Quasar / TypeScript.

So instead I have pasted the code below.

<template>
  <q-card flat  style="max-width: 1200px">
    <q-card-section>
      <q-list bordered >
        <div v-for="(thread, index) in threads" :key="thread.threadId">
          <q-expansion-item
            clickable
            :content-inset-level="1"
            @update:model-value="handleOpenClose"
          >
            <template #header>
              <q-item-section top>
                <q-item-label lines="1">
                  <span >
                    {{ thread.author.name }}
                  </span>
                  <span >
                    {{
                      date.formatDate(
                        new Date(thread.createdAt.seconds * 1000),
                        'Do MMMM hh:mmA'
                      )
                    }}
                  </span>
                </q-item-label>
                <q-item-label v-if="!isOpen" caption lines="1">
                  {{ stripHTML(thread.content) }}
                </q-item-label>
              </q-item-section>
            </template>
            <template #default>
              <q-card>
                <!-- eslint-disable-next-line vue/no-v-html -->
                <q-card-section v-html="thread.content" />
              </q-card>
            </template>
          </q-expansion-item>
          <q-separator v-if="threads && index != threads?.length - 1" />
        </div>
      </q-list>
    </q-card-section>
  </q-card>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { date } from 'quasar';

// Fake Data
const threads = ref([
  {
    threadId: '6bXb0tfCZWoNVfFEztIS',
    ticketId: '9rgz013ahc2Aqx9C2UxG',
    author: {
      name: 'Ben Bob',
      firstName: 'Ben',
      lastName: 'Bob',
      photoURL: '__vue_devtool_undefined__',
      type: 'END_USER',
    },
    createdAt: { seconds: 1656573148, nanoseconds: 38000000 },
    content: 'The first thread',
  },
  {
    threadId: 'MTM4qvzcRKQ2eZ2crpQB',
    ticketId: '9rgz013ahc2Aqx9C2UxG',
    author: {
      name: 'Ben Bob',
      firstName: 'Ben',
      lastName: 'Bob',
      photoURL: '__vue_devtool_undefined__',
      type: 'END_USER',
    },
    createdAt: { seconds: 1656573666, nanoseconds: 250000000 },
    content: 'The second thread',
  },
  {
    threadId: 'Q9xf9PFmTzs6X4LYSXXh',
    ticketId: '9rgz013ahc2Aqx9C2UxG',
    author: {
      name: 'Ben Bob',
      firstName: 'Ben',
      lastName: 'Bob',
      photoURL: null,
      type: 'END_USER',
    },
    createdAt: { seconds: 1656573990, nanoseconds: 262000000 },
    content: 'The third thread',
  },
]);

// Normal vue script code
const isOpen = ref(false);
const handleOpenClose = (isShowing: boolean) => {
  isOpen.value = isShowing;
};

const stripHTML = (html: string) => {
  let doc = new DOMParser().parseFromString(html, 'text/html');
  doc.body
    .querySelectorAll('br, li, div') // Get all <br>, <li>, and <div> elements
    .forEach((br) => br.after(doc.createTextNode(' '))); // And add spaces after them
  return doc.body.textContent || '';
};
</script>

CodePudding user response:

I'm not too familiar with Quasar, but it looks like your issue is that you're using <q-item-label v-if="!isOpen" caption lines="1">, which will always resolve true when something is open. You want to check that isOpen.value exists and equals the value of the model (which I think would be the threadId).

EDIT - added suggested answer (just a guess!):

Swap

@update:model-value="handleOpenClose"

with

@update:model-value="handleOpenClose(thread.threadId)"

and then swap

const handleOpenClose = (isShowing: boolean) => {
  isOpen.value = isShowing;
};

with

const handleOpenClose = (threadId) => {
  isOpen = threadId;
};

then swap

<q-item-label v-if="!isOpen" caption lines="1">

with

<q-item-label v-if="isOpen !== thread.threadId" caption lines="1">

  • Related