Search code examples
javascripttypescriptdatatablevuejs3primevue

Is there a way to make a row expand on clicking the row itself rather than the arrow icon in vue primevue datatable


I currently have a datatable in vue with rows and columns. I am trying to make the row itself clickable instead of the toggle arrow on the left side of the table. But I am unable to find a proper solution so far. Could you please help me out with it??

I have posted my code below

template>
  <div>
    <PV-DataTable
      :value="solutions"
      sortMode="multiple"
      stripedRows
      selectionMode="single"
      responsiveLayout="scroll"
      v-model:expandedRows="expandedRows" 
      >
      <template #empty> No solutions found </template>
      <PV-Column :expander="true" headerStyle="width: 3rem" />
      <PV-ColumnGroup type="header">
        <PV-Row>
          <PV-Column header="&nbsp;" :rowspan="2" />
          <PV-Column header="Solver" :rowspan="2" sortable />
          <PV-Column header="Runtime" :colspan="3" sortable />
        </PV-Row>
        <PV-Row>
          <PV-Column header="Total" :sortable="true" field="runtimeTotal" />
          <PV-Column
            header="Overhead"
            :sortable="true"
            field="runtimeOverhead"
          />
          <PV-Column header="QPU" :sortable="true" field="runtimeQPU" />
        </PV-Row>
      </PV-ColumnGroup>     
      <PV-Column field="solution.solver" />
      <PV-Column field="solution.runtime.total" />
      <PV-Column field="solution.runtime.overhead" />
      <PV-Column field="solution.runtime.qpu" />

      <template #expansion="slotProps" >
        <div class="solutions-subtable " >
          <PV-DataTable
            :value="getSampleWithEnergy(slotProps.data.solution)"
            responsiveLayout="scroll"
            selectionMode="single"
            v-model:selection="selectedSample"
            @rowSelect="onRowSelect"
          >
            <PV-ColumnGroup type="header" >
              <PV-Row>
                <PV-Column header="Sample" :rowspan="2"/>
                <PV-Column header="Energy" :rowspan="2" />
              </PV-Row>
            </PV-ColumnGroup>
            <PV-Column field="sample"  />
            <PV-Column field="energy" />
          </PV-DataTable>
        </div>
      </template>
    </PV-DataTable>
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue";
import axios from "@/api";
import type { Solution } from "@/types";

export default defineComponent({
  props: {
    optId: {
      type: String,
      default: "" as string,
    },
  },
  data() {
    return {
      solutions: null,
      expandedRows: [],
      selectedSample: null,
   
    };
  },
  mounted() {
    this.fetchData();
  },
  methods: {
    async fetchData() {
      await axios
        .get(`/api/optimizations/${this.optId}/solutions`)
        .then((res: { data: any; }) => {
          this.solutions = res.data;
        })
        .catch((e: { message: any; }) => {
          console.error(e);
          const content = {
            severity: "error",
            summary: "Solutions could not be loaded.",
            detail: e.message,
            life: 3000,
          };
          this.$emitter.emit("show-toast", content);
        });
    },
    getSampleWithEnergy(solutionData: Solution) {
      const samples = [];
      for (let i = 0; i < solutionData.samples.length; i++) {
        const sampleWithNums = solutionData.samples[i].map(function (
          element: boolean
        ) {
          return Number(element);
        });
        samples.push({
          sample: sampleWithNums,
          energy: solutionData.energies[i],
        });
      }
      return samples;
    },
    onRowSelect(event: any) {
      this.$toast.add({
        severity: "info",
        summary: "Sample Selected",
        detail:
          "Selected sample " +
          event.data.sample +
          ". Check Solutions Tab for more details.",
        life: 3000,
      });
      this.$emit("sampleSelected", this.selectedSample);
    },
  },
});
</script>



type here

I have tried going through the primevue datatable documentation but so far no use.


Solution

  • you can set a v-model:expandedRows in DataTable, with this you can make an array of data that you want to expand and every row expand, add to this array and after collapse remove from this array. so you can manipulate this array as you want.

    you can change it when clicking on a row with @row-click.

    @row-click pass you the data that you clicked on and the index of the row so that you can change expanded rows with this data.

    here's an example of a situation in which you want only one row to expand:

    <script setup lang="ts">
    //? vue
    import { ref } from 'vue'
    //? components
    import Column from 'primevue/column'
    import DataTable from 'primevue/datatable'
    //? Define and uses
    const expandedRow = ref([])
    //? End Define and uses
    
    function setExpandedRow($event: DataTableRowClickEvent) {
        // check if row expanded before click of not
        const isExpanded = expandedRow.value.find((p) => p.id === $event.data.id)
    
        if (isExpanded?.id) expandedRow.value = []
        else expandedPlans.value = [$event.data]
    }
    </script>
    
    <template>
        <DataTable
            v-model:expandedRows="expandedRow"
            :value="yourData"
            data-key="id"
            :paginator="false"
            @row-click="setExpandedRow"
        >
            <Column header="First Name" field="firstName" />
            <Column header="Last Name" field="lastName" />
        </DataTable>
    </template>
    

    and if you want to expand multirow you can push and splice the data that $event pass and use its index to find your data and remove it from the array.