kouts / vue-dataset

A set of Vue.js components to display datasets (lists) with filtering, paging, and sorting capabilities!
https://next--vue-dataset-demo.netlify.app/
MIT License
220 stars 26 forks source link

Filtering (<select>) on nested array #104

Closed cscomfort closed 1 year ago

cscomfort commented 1 year ago

First, thank you for publishing this great feature set -- nice work!

I've been trying to find a solution for filtering (based on a <select> value) in a nested array, e.g. in "majors" here:

{
      "name": "Environmental Science",
      "type": "BA",
      "year_of_entry": "2020–2022",
      "plan_label": "4-Year Plan",
      "majors": [
        "Environmental Science",
        "Biology"
      ]
    }

Following one of your past examples, I was looking to a computed method, filterFields(), but haven't had any luck. In my trials I also found that the previously mentioned example doesn't seem to work on Vue 3 and the latest (next) version of vue-dataset.

Any assistance getting me in going in the right direction will be much appreciated.

Cheers!

cscomfort commented 1 year ago

I've had success with the following and vue-dataset v1, where I'm able to use a <select> value to filter my data:

import {
  Dataset,
  DatasetItem
} from 'vue-dataset'

...

export default {
  components: {
    Dataset,
    DatasetItem
  },
  data() {
    return {
      degrees,
      types: {
        name: 'type',
        options: degrees.types,
        selected: ''
      },
      years: {
        name: 'year',
        options: degrees.years_of_entry,
        selected: ''
      },
      majors: {
        name: 'major',
        options: degrees.majors,
        selected: ''
      }
    }
  },
  computed: {
    filterFields() {
      const self = this
      return {
        type: this.types.selected,
        year_of_entry: this.years.selected,
        majors(value) {
          return self.majors.selected === '' || value.includes(self.majors.selected)
        }
      }
    }
  }
}
<template>
  <div>
    <dataset
      v-slot="{ ds }"
      :ds-data="degrees.list"
      :ds-filter-fields="filterFields"
    >
      <div class="select">
        <select :name="types.name" :id="types.name" v-model="types.selected">
          <option value="">All Types</option>
          <option v-for="option in types.options" :value="option">{{option}}</option>
        </select>
      </div>
      <div class="select">
        <select :name="years.name" :id="years.name" v-model="years.selected">
          <option value="">All Years</option>
          <option v-for="option in years.options" :value="option">{{option}}</option>
        </select>
      </div>
      <div class="select">
        <select :name="majors.name" :id="majors.name" v-model="majors.selected">
          <option value="">All Majors</option>
          <option v-for="option in majors.options" :value="option">{{option}}</option>
        </select>
      </div>
      <dataset-item>
        <template v-slot="{ row, rowIndex }">
          <div>
            {{ row.name }}
          </div>
        </template>
        <template v-slot:noDataFound>
          <p>No results found</p>
        </template>
      </dataset-item>
    </dataset>
  </div>
</template>

However, I've found that this same application doesn't work with the latest versions of Vue (3) and vue-dataset -- specifically the ability to filter on majors. There doesn't appear to be any reactivity when changing the associated <select> value.

Again, thanks in advance!

cscomfort commented 1 year ago

It seems that my code above was not, in fact, working as expected. However, the following update to the the computed property allows the app to work with both Vue 2 and 3:

computed: {
  filterFields() {
    const selected_major = this.majors.selected;

    return {
      type: this.types.selected,
      year_of_entry: this.years.selected,
      majors(value) {
        return selected_major === '' || value.includes(selected_major)
      }
    }
  }
}
kouts commented 1 year ago

Thanks for the explanation @cscomfort

arcadeJHS commented 1 year ago

Yes, same here. Given the fact in the source code the dsFilterFields works through a watcher:

watch(
    () => [props.dsData, dsSearch, props.dsSortby, props.dsFilterFields, props.dsSearchIn, props.dsSearchAs, props.dsSortAs],
    () => {}
)

the only way I can make it working is with a computed:

<script setup>
const currentFilterOnPendingAge = ref('');

const filterFields = computed(() => {
  const pendingAgeType = currentFilterOnPendingAge.value;
  return {
    pendingAge(value) {
      if (pendingAgeType === 'URGENT') { return value > 30; }
      if (pendingAgeType === 'NOT_URGENT') { return value <= 30; }
      return true;
    }
  }
});
</script>

<template>
<dataset :ds-filter-fields="filterFields" />
</template>