No More Posting New Topics!

If you have a question or an issue, please start a thread in our Github Discussions Forum.
This forum is closed for new threads/ topics.

Navigation

    Quasar Framework

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search

    QTable with less clutter on hierarchical data

    Framework
    2
    5
    295
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • H
      hans_meine last edited by hans_meine

      I have data that has multiple hierarchy levels; imagine each row being an entry with 3 hierarchy levels on top:

      Subject   Date        Sample   Property1  Property2
      Patient1  2018-04-12  Sample1        42g       blue
      Patient1  2018-04-12  Sample2        10g       blue
      Patient1  2019-02-24  Sample1        45g       blue
      Patient2  2018-07-01  Sample1        22g        red
      

      Now, I would like to suppress rendering cell bodies with repeated values from the previous row, at least for the first columns, similar to this:

      Subject   Date        Sample   Property1  Property2
      Patient1  2018-04-12  Sample1        42g       blue
                            Sample2        10g       blue
                2019-02-24  Sample1        45g       blue
      Patient2  2018-07-01  Sample1        22g        red
      

      While I could do this via the :data array, I would prefer to do that in the body-cell slot, in order to allow for filtering and reordering of rows (sorting is enabled). I think I could do it with the rowIndex introduced in version 1.10, by looking up the previous row and comparing with it.

      But I have two questions:

      • Would you see an obvious problem with this approach, e.g. in terms of performance? It also feels strange to get the current cell neatly unwrapped and presented by QTable, but to then add lengthy code querying the cell value from above.
      • Also, it could be a feature request for quasar / QTable itself, to suppress repeated values?
      1 Reply Last reply Reply Quote 0
      • beets
        beets last edited by beets

        If you really don’t want to use body slots, you could potentially accomplish this with the format function in the column definition. It might be slower than a body slot because I couldn’t find a reference to rowIndex in that function (it would be a nice feature if it was there) but it would be a cleaner template. I probably wouldn’t worry about speed unless it is actually slow. Note that I’m assuming each row has a unique id, say from a mysql db.

        columns: [
            {
                label: 'Patient',
                name: 'patient',
                field: 'patient',
                // ...
                format: (val, row) => {
                    const rowIndex = this.data.findIndex(item => item.id === row.id)
                    if(rowIndex === -1 || rowIndex === 0) {
                        // this row wasn't found (shouldn't happen) or it's the first row
                        return val
                    }
                    const prevRow = this.data[rowIndex - 1]
                    if(prevRow.patient === val) {
                        return ''
                    }
                    return val
                },
            }
        ]
        

        Edit: I’m also not positive how it will play with filtering. Another option is to modify the :data array as you said, but set two object properties on each row: patient and patientLabel. Then use the format function like this:

        columns: [
            {
                label: 'Patient',
                name: 'patient',
                field: 'patient',
                // ...
                format: (val, row) => row.patientLabel,
            }
        ]
        

        That will preserve sorting, but override the display.

        H 1 Reply Last reply Reply Quote 0
        • H
          hans_meine @beets last edited by hans_meine

          @beets Thanks – it’s good to know yet another option. However, I fear that an O(N²) approach (with findIndex()) is really too slow for me. To give you an impression of the size of the table – it is already kind of slow when scrolling and has a 1-2 seconds delay on sorting. (And one would also have to ensure that format() is called again when sorting / filtering, I guess?!)

          Also, it is not that I “don’t want to use body slots” – I am just wondering whether people find this use of rowIndex reasonable.

          1 Reply Last reply Reply Quote 0
          • H
            hans_meine last edited by

            I just noticed that my plan does not work: Apparently, the slot only has access to the rowIndex in the sorted and filtered table data, but not to the sorted and filtered data array itself! 😞

            H 1 Reply Last reply Reply Quote 0
            • H
              hans_meine @hans_meine last edited by

              Thanks to @rstoenescu himself (who replied on GitHub, pointing me to the filteredSortedRows computed property), I found a solution that works fine.

              Simplified code:

                    <template>
                      <div class="... q-pa-md">
                        <q-scroll-area class="col row">
                          <q-table
                            ref="myTable"
                            :data="tableData"
                            :columns="columns"
                            :pagination.sync="pagination"
                          >
                            <template v-slot:body-cell="props">
                              <q-td :props="props">
                                {{ deduplicatedCellValue(props) }}
                              </q-td>
                            </template>
                          </q-table>
                        </q-scroll-area>
                      </div>
                    </template>
                    
                    <script>
                    export default {
                      data() {
                        return {
                          tableData: [
                              ...
                          ],
                          columns: [
                            {
                              name: "subject",
                              label: "Subject",
                              field: "subject",
                              sortable: true,
                              deduplicate: true,
                            },
                            {
                              name: "date",
                              label: "Date",
                              field: "date",
                              sortable: true,
                              deduplicate: true,
                            },
                            {
                              name: "sample",
                              label: "Sample",
                              field: "sample",
                              sortable: true,
                              deduplicate: true,
                            },
                            ...
                          ],
                          pagination: {
                            rowsPerPage: 0,
                            sortBy: "subject",
                          },
                        };
                      },
                      methods: {
                        deduplicatedCellValue(props) {
                          if (props.rowIndex > 0 && props.col.deduplicate) {
                            let table = this.$refs.myTable.filteredSortedRows;
                            if (props.value == table[props.rowIndex - 1][props.col.field]) {
                              return "";
                            }
                          }
                          return props.value;
                        },
                      },
                    };
                    </script>
              

              It does not seem to come with a big performance penalty, although the previous row has to be computed for every cell anew. However, I had introduced a “deduplicate” column flag anyhow, because I only wanted to do this on the header columns, but not remove identical property values from the later columns. Therefore, the dereferencing of this.$refs.myTable.filteredSortedRows[props.rowIndex - 1] has to be done only for three cells per row, which is acceptable.

              Still, I wonder if QTable should possibly introduce a props.previousRow next to props.row. To me, that would mean much less code on my side, and a possibly slightly more performant solution in the end. But I understand that every feature in Quasar is something that needs to be documented and maintained as well, so I won’t push for it.

              1 Reply Last reply Reply Quote 0
              • First post
                Last post