Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FIltering results based on several Multiselect boxes, vue

I have a vue component where I'm currently successfully showing results from a data object, and I've also successfully created several multiselect boxes. My issue is filtering.

I know how I can set a single value from the multiselect and compare it (using v-if) in order to show certain results in an HTML div, but I'm completely lost now on how to do proper filtering based on multiple Multiselects (especially since several of them allow multiple options that store the values in arrays)

I'm putting my snippet below, but how can I properly make it so that I can filter results based on all values in the corresponding v-models for the multiselects, while making sure that if "All stores" or "All areas" is selected, it allows all values for that selection?

-- In other words, if the user doesn't ake a selection and the multiselect is left on the placeholder, all values for that select would be allowed to show in the DOM (based on other filters first)

new Vue({
  el: "#app",
  components: {Multiselect: window.VueMultiselect.default},
  data: {
    selectedOutput: '',
    selectedAreas:[],
    selectedStores: [],
    selectedCategories: [],
    selectedShifts: [],
    shifts: [
      {id: 1, name: "First"},
      {id: 2, name: "Second"}
    ],
    categories: [
      {id: 1, name: "electronics"},
      {id: 1, name: "home"},
      {id: 1, name: "auto"},
    ],
    outputOptions: [
      {id:1, name: "Sold"},
      {id:2, name: "Purchased"}
    ],
    areas: [
        {value: 1, name: "East"},
        {value: 1, name: "West"},
    ],
    stores: [
        {value: 1, name: "One"},
        {value: 2, name: "Two"}
    ],
    workNumbers: [
        {
          "Adam": {
            "name": "Adam",
            "title": "Manager",
            "shift": "First",
            "category": "electronics",
            "area" : "East",
            "store": "One",
            "sold": 140,
            "purchased": 15
          },
          "Ben": {
            "name": "Ben",
            "title": "Manager",
            "shift": "First",
            "category": "electronics",
            "area" : "East",
            "store": "One",
            "sold": 225,
            "purchased": 77
          },
          "Suzie": {
            "name": "Suzie",
            "title": "Manager",
            "shift": "Second",
            "category": "home",
            "area" : "West",
            "store": "Two",
            "sold": 124,
            "purchased": 55
          },
          "Reg": {
            "name": "Reg",
            "title": "Manager",
            "shift": "Second",
            "category": "home",
            "area" : "West",
            "store": "Two",
            "sold": 66,
            "purchased": 36
          },
          "Kelly": {
            "name": "Kelly",
            "title": "Manager",
            "shift": "Second",
            "category": "home",
            "area" : "West",
            "store": "Two",
            "sold": 55,
            "purchased": 2
          },
        }
    ]
  },
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-multiselect/3.0.0-alpha.2/vue-multiselect.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/vue-multiselect/3.0.0-alpha.2/dist/vue-multiselect.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="uk-width-2-10" style="position: relative !important;">
    <multiselect
      v-model="selectedOutput"
      :options="outputOptions"
      :multiple="false"
      :close-on-select="true"
      label="name"
      track-by="name"
      @input="checkOutput"
    ></multiselect>
</div>
<div class="uk-width-2-10" style="position:relative;">
    <multiselect
      v-model="selectedShifts"
      :options="shifts"
      :multiple="true"
      :close-on-select="true"
      placeholder="All shifts"
      label="name"
      track-by="name"
    ></multiselect>
</div>
<div class="uk-width-2-10" style="position: relative !important;">
    <multiselect
      v-model="selectedCategories"
      :options="categories"
      :multiple="true"
      :close-on-select="true"
      placeholder="All categories"
      label="name"
      track-by="id"
    ></multiselect>
</div>
<div class="uk-width-2-10" style="position: relative !important;">
    <multiselect
      v-model="selectedAreas"
      :options="areas"
      :multiple="true"
      :close-on-select="true"
      placeholder="All areas"
      label="name"
      track-by="name"
    ></multiselect>
</div>
<div class="uk-width-2-10" style="position: relative !important;">
    <multiselect
      v-model="selectedstores"
      :options="stores"
      :multiple="true"
      :close-on-select="true"
      placeholder="All stores"
      label="name"
      track-by="value"
    ></multiselect>
</div>

    <table>
    <tbody v-if="selectedOutput.name === 'Sold'">
      <tr v-for="(value, employee) in workNumbers"  :key="employee">
        <!-- this is where I need a condition to show based on filters, I believe-->
        <td>{{name}} - {{sold}}</td>
      </tr>
    </tbody>
    <tbody v-else-if="selectedOutput.name === 'Purchased'">
      <tr v-for="(value, employee) in workNumbers"  :key="employee">
        <!-- this is where I need a condition to show based on filters, I believe-->
        <td>{{name}} - {{purchased}}</td>
      </tr>
    </tbody>
    </table>
</div>

UPDATE:

Now I've moved to another table which is similar but it's looping around different objects in order to create a different model that is also passed through a function for a modal.

<tbody v-if="selectedOutput.name === 'Cubes'">
  <tr v-for="(value, employee) in workNumbers"  :key="employee">
    <td v-for="date in dates" :key="date" >
      <div v-for="(dateSpecificData, dateValue) in value.dates" :key="dateValue" @click="showModal(dateSpecificData)"   :style="'background: ' + (dateSpecificData.unavailable > 0 ? '#f7a7a3' : '#a8f0c6')">
        <div v-if="dateValue == date ">
          @{{dateSpecificData.sold}}
        </div>
      </div>
    </td>
  </tr>
</tbody>
like image 700
Geoff_S Avatar asked Oct 07 '21 12:10

Geoff_S


1 Answers

You can define a computed-property that returns the filtered list according to the params:

new Vue({
  el: "#app",
  components: { Multiselect: window.VueMultiselect.default },
  data: () => ({
    selectedOutput: '',
    outputOptions: [ {id:1, name: "Sold"}, {id:2, name: "Purchased"} ],
    selectedShifts: [],
    shifts: [ {id: 1, name: "First"}, {id: 2, name: "Second"} ],
    selectedCategories: [],
    categories: [ {id: 1, name: "electronics"}, {id: 2, name: "home"}, {id: 3, name: "auto"} ],
    selectedAreas:[],
    areas: [ {value: 1, name: "East"}, {value: 1, name: "West"} ],
    selectedStores: [],
    stores: [ {value: 1, name: "One"}, {value: 2, name: "Two"} ],
    workNumbers: [
      {
        "Adam": { "name": "Adam", "title": "Manager", "shift": "First", "category": "electronics", "area" : "East", "store": "One", "sold": 140, "purchased": 15 },
        "Ben": { "name": "Ben", "title": "Manager", "shift": "First", "category": "home", "area" : "West", "store": "Two", "sold": 225, "purchased": 77 },
        "Suzie": { "name": "Suzie", "title": "Manager", "shift": "Second", "category": "electronics", "area" : "East", "store": "One", "sold": 124, "purchased": 55 },
        "Reg": { "name": "Reg", "title": "Manager", "shift": "Second", "category": "home", "area" : "West", "store": "Two", "sold": 66, "purchased": 36 },
        "Kelly": { "name": "Kelly", "title": "Manager", "shift": "Second", "category": "auto", "area" : "West", "store": "Two", "sold": 55, "purchased": 2 }
      }
    ]
  }),
  methods: {
    filtedSelectedHelper(arr = [], val) {
      return arr.length ? arr.some(({ name }) => name === val) : true;
    }
  },
  computed: {
    filteredWorkNumbers () {
      const ouput = this.selectedOutput;
      const filteredList = 
        this.workNumbers
          .flatMap(Object.values)
          .filter(({ shift, category, area, store }) => 
            this.filtedSelectedHelper(this.selectedShifts, shift) &&
            this.filtedSelectedHelper(this.selectedCategories, category) &&
            this.filtedSelectedHelper(this.selectedAreas, area) &&
            this.filtedSelectedHelper(this.selectedStores, store) 
          );
       return !this.selectedOutput 
         ? filteredList.map(({ name, sold, purchased }) => 
             `${name} - ${sold} - ${purchased}`
           )
         : this.selectedOutput.name === "Sold"
           ? filteredList.map(({ name, sold }) => 
               `${name} - ${sold}`
             )
           : filteredList.map(({ name, purchased }) => 
               `${name} - ${purchased}`
             )
    }
  }
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/[email protected]"></script>
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/vue-multiselect.min.css">

<div id="app">
  <div class="uk-width-2-10" style="position: relative !important;">
    <multiselect
      v-model="selectedOutput"
      :options="outputOptions"
      :multiple="false"
      :close-on-select="true"
      label="name"
      track-by="name"
    ></multiselect>
  </div>
  <div class="uk-width-2-10" style="position:relative;">
    <multiselect
      v-model="selectedShifts"
      :options="shifts"
      :multiple="true"
      :close-on-select="true"
      placeholder="All shifts"
      label="name"
      track-by="name"
    ></multiselect>
  </div>
  <div class="uk-width-2-10" style="position: relative !important;">
    <multiselect
      v-model="selectedCategories"
      :options="categories"
      :multiple="true"
      :close-on-select="true"
      placeholder="All categories"
      label="name"
      track-by="id"
    ></multiselect>
  </div>
  <div class="uk-width-2-10" style="position: relative !important;">
    <multiselect
      v-model="selectedAreas"
      :options="areas"
      :multiple="true"
      :close-on-select="true"
      placeholder="All areas"
      label="name"
      track-by="name"
    ></multiselect>
  </div>
  <div class="uk-width-2-10" style="position: relative !important;">
    <multiselect
      v-model="selectedStores"
      :options="stores"
      :multiple="true"
      :close-on-select="true"
      placeholder="All stores"
      label="name"
      track-by="value"
    ></multiselect>
  </div>
  <table>
    <tbody>
      <tr v-for="str in filteredWorkNumbers" :key="str"><td>{{str}}</td></tr>
    </tbody>
  </table>
</div>

Resources:

https://v2.vuejs.org/v2/guide/computed.html

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

like image 169
Majed Badawi Avatar answered Oct 08 '22 06:10

Majed Badawi