Как сделать фильтрацию по нескольким значениям в VueJS?

Задача в том, чтоб фильтровать выданные результаты по нескольким значениям для уже полученного результата и сбрасывать сам фильтр кнопкой.

Фильтр:
<h6>{{ trans('calculator.filter') }}</h6>

    <div class="row">
      <div class="col">
    <p>{{ trans('calculator.brand_filter') }}</p>
    <select v-model="brand" class="custom-select">
      <option v-for="brand in brands.sort()" :value="brand">{{ brand }}</option>
    </select>
      </div>

      <div class="col">
    <p>{{ trans('calculator.company_filter') }}</p>
    <select  v-model="company" class="custom-select">
      <option v-for="company in companies.sort()" :value="company">{{ company }}</option>
    </select></div>


      <div class="col">
    <p>{{ trans('calculator.country_filter') }}</p>
    <select v-model="country" class="custom-select">
      <option v-for="brand in countries.sort()" :value="brand">{{ brand }}</option>
    </select></div>

        <div class="col">
    <p>{{ trans('calculator.side_filter') }}</p>
    <select v-model="sidesFilter" class="custom-select">
      <option v-for="side in sides.sort()" :value="side">{{ side }}</option>
    </select></div>

          <div class="col">
    <p>{{ trans('calculator.rating_filter') }}</p>
    <select v-model="ratingFilter" class="custom-select" disabled>
      <option value="5" selected>5</option>
      <option value="4">4</option>
      <option value="3">3</option>
      <option value="2">2</option>
      <option value="1">1</option>
    </select></div>

      <a href="#">{{ trans('calculator.reset_filter') }}</a></div>


Полученные результаты:
<div v-if="!profiles.length">
      <h4>{{ trans('calculator.no_results') }}</h4>
    </div>
    <div v-for="profile in filteredProfiles" class="row feed">
      <div class="col-3">
        <img alt="" :src="`${profile.company.logo}`" width="100%">
      </div>
      <div class="col-5 no-padding">
        <a :href="`/companies/${profile.company.id}`" target="_blank" class="title">{{profile.company.name}}</a>
        <div>
          <star-rating
            v-model="profile.company.rating || 0"
            read-only
            :show-rating="false"
            :star-size="16"
          />
          <span>{{(profile.company.workingHours.isOpen) ? trans('calculator.open') : trans('calculator.closed') }} | </span>
          <span>
            {{roundTime(profile.company.workingHours.today.from)}}
            -
            {{roundTime(profile.company.workingHours.today.to)}}</span>
        </div>
        <span>{{ trans('calculator.brand') }}: {{profile.brand.name}}<br>{{ trans('calculator.country') }}: {{profile.brand.country}}</span>
        <p class="text-muted">{{`${profile.sides}`}} {{ trans('calculator.profile_window') }}</p>
      </div>
      <div class="col-4">
        <h5>{{ trans('calculator.price') }}: {{Math.round(profile.price)}} </h5>
        <p>*{{ trans('calculator.pre_price') }}&nbsp;<i v-tooltip.top="trans('calculator.info_preprice')" class="fas fa-info-circle"></i></p>
        <br>
        <div class="responsive_results">
        <button
          class="button_calculator coolBeans"
          @click="selectProfile(profile)"
          data-toggle="modal"
          data-target="#orderModal"
        >
          {{ trans('calculator.order') }}
        </button></div>
      </div>
    </div>


Логика:
export default {
    props: ['profiles', 'onSend'],
    data() {
      return {
        isModalOpen: false,
        profile: {},
      }
    },
    computed:   {

      filteredProfiles() {
        return this.profiles.filter(
                n => n.brand.name === this.brand,
                n => n.company.name === this.company,
                n => n.brand.country === this.country,
                n => n.sides === this.side
      },

      brands() {
        return [...new Set(this.profiles.map(n => n.brand.name))];
      },
      companies() {
        return [...new Set(this.profiles.map(n => n.company.name))];
      },
      countries() {
        return [...new Set(this.profiles.map(n => n.brand.country))];
      },
      sides() {
        return [...new Set(this.profiles.map(n => n.sides))];
      },

    },

    methods: {

      roundTime(time) {
        const timeStr = time.split(':');
        timeStr.pop();
        return timeStr.join(':');
      },

      selectProfile(profile) {
        this.profile = profile;
      },

      orderProfile(profile) {
        $('#orderModal').modal('hide');
        this.onSend(profile);
      }
    }
  }


Как прописать логику filteredProfiles чтоб с помощью фильтра фильтровать показанный результат? И как прописать кнопку reset
<a href="#">{{ trans('calculator.reset_filter') }}</a>

Чтоб сбросить фильтр
  • Вопрос задан
  • 4423 просмотра
Решения вопроса 1
0xD34F
@0xD34F Куратор тега Vue.js
Сложно это всё. Четыре селекта - очень похожие, четыре вычисляемых свойства - очень похожие. Многовато копипасты. Надо упростить.

Первым делом внимательно посмотрим на все эти похожие куски кода, найдём, где они отличаются, и на основе этих отличий сделаем описание фильтров:

filters: [
  { name: 'calculator.brand_filter', getter: obj => obj.brand.name, value: '' },
  { name: 'calculator.company_filter', getter: obj => obj.company.name, value: '' },
  { name: 'calculator.country_filter', getter: obj => obj.brand.country, value: '' },
  { name: 'calculator.side_filter', getter: obj => obj.sides, value: '' },
],

Затем сделаем собственно фильтрацию, пропустим данные через массив фильтров (если значение фильтра выставлено - производится фильтрация, нет - оставляем данные как есть):

computed: {
  filteredProfiles() {
    return this.filters.reduce((profiles, { value, getter }) => {
      return value
        ? profiles.filter(n => getter(n) === value)
        : profiles;
    }, this.profiles);
  },

Далее, надо дать пользователю возможность задавать значения фильтров. Сделаем компонент фильтра:

Vue.component('filter-select', {
  props: [ 'name', 'options', 'value' ],
  template: `
<div>
  <p>{{ name }}<p>
  <select :value="value" @change="$emit('input', $event.target.value)">
    <option value="">-</option>
    <option v-for="n in options">{{ n }}</option>
  </select>
</div>`
});

Подготовим опции для фильтров - массивы уникальных значений, по которым будет осуществляться фильтрация:

computed: {
  filterOptions() {
    return this.filters.map(n => [...new Set(this.profiles.map(n.getter))]);
  },

Наконец, создадим сами фильтры:

<filter-select
  v-for="(n, i) in filters"
  v-model="n.value"
  :options="filterOptions[i]"
  :name="trans(n.name)"
></filter-select>

Также не будем забывать про обнуление значений фильтров. Так что вот метод сброса фильтров и кнопка, по клику на которую он будет вызываться:

methods: {
  resetFilters() {
    this.filters.forEach(n => n.value = '');
  },

<button @click="resetFilters">{{ trans('calculator.reset_filter') }}</button>

Демо можно посмотреть здесь (да, тут меньше фильтров, чем у вас - ну так вы свои данные не показали, а у меня воображения на большее не хватило).
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы