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

    Trouble with collecting selected items from QTable

    Help
    table
    2
    3
    1536
    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.
    • F
      fenchai last edited by fenchai

      OK I lied, but not.

      I have a QTable with multi select, it works fine. but the project was getting a bit too big so I decided to separate into modules. But now I would need to somehow pass the selected data into the module(s).

      I decided to use Vuex for this.

      The Problem:

      The Qtable does not have an API for detecting what items are selected or deselected. There are 2 API Events for Selections:

      1. @selection -> function(details) This one is not very useful because it only detects what item got selected or deselected. It triggers before the array of selection is modified. I am not able to get the modified array. (maybe I could with some workaround but is this the way?)

      2. @update:selected -> function(newSelected) This one is the one QTable uses for updating the selected item’s array. The problem with t his one is that it does not triggers when the array is empty.

      My current template is using @selection -> function(details) method to update the selected array in vuex store but there is a delay of 1 change because the method triggers before the array is modified.

      <template>
        <div>
          <q-table
            ref="mainTable"
            class="my-sticky-virtscroll-table"
            style="height: 800px"
            :data="tableData"
            :columns="columns"
            row-key="CODIGO"
            :selected-rows-label="getSelectedString"
            selection="multiple"
            :selected.sync="selected"
            virtual-scroll
            :pagination.sync="pagination"
            :rows-per-page-options="[0]"
            :virtual-scroll-sticky-size-start="48"
            :filter="term"
            flat
            bordered
            @focusin.native="activateNavigation"
            @focusout.native="deactivateNavigation"
            @keydown.native="onKey"
            @selection="onSelect"
          >
            <template v-slot:top-left>
              <q-input
                ref="mainSearchInput"
                debounce="0"
                v-model="term"
                label="Búsqueda"
                filled
                bottom-slots
                clearable=""
                style="width:500px"
                @input="updateSearchTerm(term)"
              >
                <template v-slot:hint
                  >Puede hacer búsquedas online con commandos.</template
                >
              </q-input>
            </template>
      
            <template v-slot:body-cell-qty="props">
              <q-td :props="props">
                <q-badge
                  :class="{
                    'bg-black': props.value <= 0,
                    'bg-red': props.value > 0,
                    'bg-orange': props.value >= 100 && props.value < 500,
                    'bg-green': props.value >= 500,
                    'text-body1': true
                  }"
                  :label="props.value"
                />
              </q-td>
            </template>
      
            <template v-slot:body-cell-code="props">
              <q-td :props="props">
                <div class="tableCell">{{ props.value }}</div>
              </q-td>
            </template>
      
            <template v-slot:body-cell-description="props">
              <q-td :props="props" :style="{ width: '50px', whiteSpace: 'normal' }">
                <div class="tableCell">
                  {{ props.value }}
                </div>
              </q-td>
            </template>
      
            <!-- <template v-slot:body-cell-qty="props">
              <q-td :props="props">
                <div class="tableCell">{{ props.value }}</div>
              </q-td>
            </template> -->
      
            <template v-slot:body-cell-codAlt="props">
              <q-td :props="props" :style="{ width: '50px', whiteSpace: 'normal' }">
                <div class="tableCell">{{ props.value }}</div>
              </q-td>
            </template>
      
            <template v-slot:body-cell-desAlt="props">
              <q-td :props="props" :style="{ width: '50px', whiteSpace: 'normal' }">
                <div class="tableCell">{{ props.value }}</div>
              </q-td>
            </template>
      
            <template v-slot:body-cell-group="props">
              <q-td :props="props">
                <div class="tableCell">{{ props.value }}</div>
              </q-td>
            </template>
      
            <template v-slot:body-cell-price="props">
              <q-td :props="props">
                <div class="tableCell">{{ props.value }}</div>
              </q-td>
            </template>
      
            <template v-slot:body-cell-discount20="props">
              <q-td :props="props">
                <div class="tableCell">{{ props.value }}</div>
              </q-td>
            </template>
          </q-table>
          <!-- <div class="q-mt-md">Selected: {{ JSON.stringify(selected) }}</div> -->
          <chipDetails></chipDetails>
        </div>
      </template>
      
      <script>
      import path from "path";
      import { remote } from "electron";
      import { Platform } from "quasar";
      import { mapGetters, mapActions } from "vuex";
      
      export default {
        computed: {
          ...mapGetters("central", ["searchTerm", "selectedTableData"])
        },
      
        components: {
          chipDetails: require("components/chipDetails.vue").default
        },
      
        data() {
          return {
            term: "",
            tableData: [],
            selected: [],
            selectedTableData: [],
            navigationActive: false,
            pagination: {
              rowsPerPage: 300
            },
            columns: [
              {
                name: "code",
                required: true,
                label: "Código",
                align: "left",
                // field: row => row.name,
                field: "CODIGO",
                format: val => `${val}`,
                sortable: true
              },
              {
                name: "description",
                align: "left",
                label: "Descipción",
                field: "DESCRIPCION",
                sortable: true
              },
              {
                name: "qty",
                label: "Qty",
                field: "INVENT",
                sortable: true,
                align: "right"
              },
              { name: "codAlt", label: "Cod Alt", field: "COD.ALT.", align: "left" },
              {
                name: "desAlt",
                label: "Desc Alt",
                field: "DESC.ALT.",
                align: "left"
              },
              {
                name: "group",
                label: "Grupo",
                field: "GRUPO",
                align: "left"
              },
              { name: "price", label: "Precio", field: "PRECIO", align: "right" },
              {
                name: "discount20",
                label: "20 %",
                field: "PRECIO",
                align: "right",
                sort: (a, b) => parseInt(a, 10) - parseInt(b, 10)
              }
            ]
          };
        },
      
        methods: {
          ...mapActions("central", ["updateSearchTerm", "updateSelectedTableData"]),
      
          getSelectedString() {
            return (
              this.selected.length +
              " selecionado" +
              (this.selected.length > 1 ? "s" : "") +
              " de " +
              this.tableData.length
            );
          },
      
          activateNavigation() {
            this.navigationActive = true;
          },
      
          deactivateNavigation() {
            this.navigationActive = false;
          },
      
          onSelect(evt) {
            console.log(evt);
            this.updateSelectedTableData(this.selected);
            console.log(this.selected);
          },
      
          onKey(evt) {
            if (
              this.navigationActive !== true ||
              [33, 34, 35, 36, 38, 40].indexOf(evt.keyCode) === -1 ||
              this.$refs.mainTable === void 0
            ) {
              return;
            }
      
            evt.preventDefault();
      
            switch (evt.keyCode) {
              case 36: // Home
                page = 1;
                index = 0;
                break;
              case 35: // End
                page = lastPage;
                index = rowsPerPage - 1;
                break;
              case 33: // PageUp
                page = currentPage <= 1 ? lastPage : currentPage - 1;
                if (index < 0) {
                  index = 0;
                }
                break;
              case 34: // PageDown
                page = currentPage >= lastPage ? 1 : currentPage + 1;
                if (index < 0) {
                  index = rowsPerPage - 1;
                }
                break;
              case 38: // ArrowUp
                if (currentIndex <= 0) {
                  page = currentPage <= 1 ? lastPage : currentPage - 1;
                  index = rowsPerPage - 1;
                } else {
                  index = currentIndex - 1;
                }
                break;
              case 40: // ArrowDown
                if (currentIndex >= lastIndex) {
                  page = currentPage >= lastPage ? 1 : currentPage + 1;
                  index = 0;
                } else {
                  index = currentIndex + 1;
                }
                break;
            }
      
            if (page !== this.pagination.page) {
              this.pagination = {
                ...this.pagination,
                page
              };
      
              this.$nextTick(() => {
                const { computedRows } = this.$refs.mainTable;
                this.selected = [
                  computedRows[Math.min(index, computedRows.length - 1)]
                ];
              });
            } else {
              this.selected = [computedRows[index]];
            }
          }
        },
      
        mounted() {
          if (Platform.is.electron) {
            // get file path
            const filePath = path.join(
              remote.app.getPath("home"),
              "/Dropbox/Sync/inventarioHL.json"
            );
      
            // load the File System to execute our common tasks (CRUD)
            var fs = require("fs");
      
            // read file content
            fs.readFile(filePath, "utf-8", (err, getData) => {
              if (err) {
                alert("An error ocurred reading the file :" + err.message);
              }
              // parse text into json
              var jsonData = JSON.parse(getData);
      
              // add entire data to table
              this.tableData = jsonData;
      
              // console.log(jsonData[0]);
      
              // focus on q-input
              this.$refs.mainSearchInput.$el.focus();
            });
          }
      
          // update the q-input with searchTerm
          this.term = this.searchTerm;
        }
      };
      </script>
      
      <style lang="sass">
      .my-sticky-virtscroll-table
        /* height or max-height is important */
        height: 310px
      
        /* specifying max-width so the example can
          highlight the sticky column on any browser window */
        max-width: 1300px
      
        td:first-child
          /* bg color is important for td; just specify one */
          background-color: #fafafa !important
      
        tr th
          position: sticky
          /* higher than z-index for td below */
          z-index: 2
          /* bg color is important; just specify one */
          background: #fafafa
      
        /* this will be the loading indicator */
        thead tr:last-child th
          /* height of all previous header rows */
          top: 48px
          /* highest z-index */
          z-index: 3
        thead tr:first-child th
          top: 0
          z-index: 1
        tr:first-child th:first-child
          /* highest z-index */
          z-index: 3
      
        td:first-child
          z-index: 1
      
        td:first-child, th:first-child
          position: sticky
          left: 0
      </style>
      
      <style>
      .tableCell {
        font-size: 18px;
      }
      </style>
      

      This is the component I am using to access the selected array

      <template>
        <div class="q-pt-sm">
          <q-chip
            color="secondary"
            text-color="white"
            v-for="(sel, index) in selectedTableData"
            :key="index"
            :label="sel['CODIGO']"
            removable
            @remove="removeSelectedChip(index)"
            clickable
            @click="fixed = true"
          />
          <q-dialog v-model="fixed">
            <q-card>
              <q-card-section>
                <div class="text-h6">Terms of Agreement</div>
              </q-card-section>
      
              <q-separator />
      
              <q-card-section style="max-height: 50vh" class="scroll">
                <p v-for="n in 15" :key="n">
                  Lorem ipsum dolor sit amet consectetur adipisicing elit. Rerum
                  repellendus sit voluptate voluptas eveniet porro. Rerum blanditiis
                  perferendis totam, ea at omnis vel numquam exercitationem aut, natus
                  minima, porro labore.
                </p>
              </q-card-section>
      
              <q-separator />
      
              <q-card-actions align="right">
                <q-btn flat label="..." color="primary" v-close-popup />
                <q-btn flat label="OK" color="primary" v-close-popup />
              </q-card-actions>
            </q-card>
          </q-dialog>
        </div>
      </template>
      
      <script>
      import { mapGetters } from "vuex";
      export default {
        data() {
          return {
            fixed: false
          };
        },
        computed: {
          ...mapGetters("central", ["selectedTableData"])
        }
      };
      </script>
      

      Finally this is the vuex store

      const state = {
        searchTerm: "",
        selectedTableData: []
      };
      
      const mutations = {
        updateSearchTerm(state, term) {
          state.searchTerm = term;
        },
        updateSelectedTableData(state, data) {
          state.selectedTableData = data;
        }
      };
      
      const actions = {
        updateSearchTerm({ commit }, term) {
          commit("updateSearchTerm", term);
        },
        updateSelectedTableData({ commit }, data) {
          commit("updateSelectedTableData", data);
        }
      };
      
      const getters = {
        searchTerm: state => {
          return state.searchTerm;
        },
        selectedTableData: state => {
          return state.selectedTableData;
        }
      };
      
      export default {
        namespaced: true,
        state,
        mutations,
        actions,
        getters
      };
      
      1 Reply Last reply Reply Quote 0
      • T
        tof06 last edited by

        @fenchai

        You can use a 2-way computed property for your selected array :

        computed: {
          selected: {
            get() {
              return this.$store.state.central.selectedTableData
            },
           set(val) {
             this.$store.commit('central/updateSelectedTableData', val)
           }
         }
        

        And of course, remove selected property in data()
        This way, your vuex state will always be in sync with your selection.

        You can also have a look to vuex-map-fields library, which will create getters&setters for you 🙂

        1 Reply Last reply Reply Quote 1
        • F
          fenchai last edited by

          @tof06 I thought this

          computed: {
              ...mapGetters("central", ["searchTerm", "selectedTableData"])
            },
          

          already did the job.

          I guess I will use 2-way-computed thanks for the info

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