Search code examples
javascripthtmlvue.jsvuejs2dropdown

How to filter items based on multiple select dropdown with Vue JS?


I'm learning vue.js and I'm trying to make a case study search with multiple dropdowns and button search. I need to find out a filter function that will filter the object list based on a selection from several dropdowns with the trigger button search.

I have the code that I have written below, but this filter works in realtime. When one of the dropdowns is selected it will display the search results. But what I want is that the search results appear when after selecting several dropdowns and must click the search button, then the results appear.

I've been looking for some references and tried but still not getting the results I want.

new Vue({
  el: '#app',
  data: {
    selectedType: '',
    selectedCountry: '',
    selectedYear: '',
    items: [
      {
        name: 'Nolan',
        type: 'mercedes',
        year: '2020',
        country: 'england'
      },
      {
        name: 'Edgar',
        type: 'bmw',
        year: '2020',
        country:'belgium'
      },
      {
        name: 'John',
        type: 'bmw',
        year: '2019',
        country: 'england'
      },
      {
        name: 'Axel',
        type: 'mercedes',
        year: '2020',
        country: 'england'
      }
    ]
  },
  computed: {
    searchItem: function () {
      let filterType= this.selectedType,
          filterCountry = this.selectedCountry,
          filterYear = this.selectedYear
      
      return this.items.filter(function(item){
        let filtered = true
        if(filterType && filterType.length > 0){
          filtered = item.type == filterType
        }
        if(filtered){
          if(filterCountry && filterCountry.length > 0){
            filtered = item.country == filterCountry
          }
        }
        if(filtered){
          if(filterYear && filterYear.length > 0){
            filtered = item.year == filterYear
          }
        }
        return filtered
      })
    }
  }
})
.list-item{
  margin-top:50px;
}

.card{
  box-shadow:0px 10px 16px rgba(0,0,0,0.16);
  width:400px;
  padding:20px 30px;
  margin-bottom:30px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.1/vue.js"></script>

<div id="app">
  <select v-model="selectedType">
    <option value="mercedes">Mercedes</option>
    <option value="bmw">BMW</option>
  </select>
  
  <select v-model="selectedCountry">
    <option value="belgium">Belgium</option>
    <option value="england">England</option>
  </select>
  
  <select v-model="selectedYear">
    <option value="2019">2019</option>
    <option value="2020">2020</option>
  </select>
  
  <button>Search</button>
  
  <div class="list-item" v-for="item in searchItem">
    <div class="card">
      <p>Name: {{ item.name }}</p>
      <p>Car: {{ item.type }}</p>
      <p>Year: {{ item.year }}</p>
      <p>Country: {{ item.country }}</p>
    </div>
  </div>
</div>


Solution

  • Instead of using computed, consider defining a method for the filtering.

    See the following as an example

    new Vue({
      el: '#app',
      data: {
        selectedType: '',
        selectedCountry: '',
        selectedYear: '',
        items: [{
            name: 'Nolan',
            type: 'mercedes',
            year: '2020',
            country: 'england'
          },
          {
            name: 'Edgar',
            type: 'bmw',
            year: '2020',
            country: 'belgium'
          },
          {
            name: 'John',
            type: 'bmw',
            year: '2019',
            country: 'england'
          },
          {
            name: 'Axel',
            type: 'mercedes',
            year: '2020',
            country: 'england'
          }
        ],
        searchResult: [],
      },
      methods: {
        search: function() {
          let filterType = this.selectedType,
            filterCountry = this.selectedCountry,
            filterYear = this.selectedYear
    
          this.searchResult = this.items.filter(function(item) {
            let filtered = true
            if (filterType && filterType.length > 0) {
              filtered = item.type == filterType
            }
            if (filtered) {
              if (filterCountry && filterCountry.length > 0) {
                filtered = item.country == filterCountry
              }
            }
            if (filtered) {
              if (filterYear && filterYear.length > 0) {
                filtered = item.year == filterYear
              }
            }
            return filtered
          })
        }
      },
      mounted() {
        this.search()
      }
    })
    .list-item {
      margin-top: 50px;
    }
    
    .card {
      box-shadow: 0px 10px 16px rgba(0, 0, 0, 0.16);
      width: 400px;
      padding: 20px 30px;
      margin-bottom: 30px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.1/vue.js"></script>
    
    <div id="app">
      <select v-model="selectedType">
        <option value="" disabled selected hidden>Type</option>
        <option value="mercedes">Mercedes</option>
        <option value="bmw">BMW</option>
      </select>
    
      <select v-model="selectedCountry">
        <option value="" disabled selected hidden>Country</option>
        <option value="belgium">Belgium</option>
        <option value="england">England</option>
      </select>
    
      <select v-model="selectedYear">
        <option value="" disabled selected hidden>Year</option>
        <option value="2019">2019</option>
        <option value="2020">2020</option>
      </select>
    
      <button @click="search">Search</button>
    
      <div class="list-item" v-for="item in searchResult">
        <div class="card">
          <p>Name: {{ item.name }}</p>
          <p>Car: {{ item.type }}</p>
          <p>Year: {{ item.year }}</p>
          <p>Country: {{ item.country }}</p>
        </div>
      </div>
    </div>