0

If this is a duplicate post I apologize. I searched a couple different things before posting this. I'm trying to figure out how to filter results based off of two options. I can get one of the options to work but need both to drive the results.

I have 4 computed properties:

  • filteredResults: where the filtering is taking place
  • phases: collection of all the phases based on the results
  • results: original results list (stored in Vuex)
  • states: collection of all the states based on the results

In the data on that component I have two properties that are binded to what the user selects. I'm running the filtering off of those selected values.

Code

<template>
  <div class="results">
    <Banner/>
    <div class="container">
      <div class="columns">
        <main role="main" class="column is-8-tablet is-9-widescreen">
          <p class="title is-4">Results for: {{ $route.query.q }}</p>
          <p class="subtitle">{{ results.length }} Trials Avaliable</p>
          <ul>
            <li v-for="trial in results" :key="trial.studyid">
              <div class="card" :data-location="trial.state" :data-phases="trial.phasename">
                <div class="card-content">
                  <div class="content">
                    <h2 class="title is-4">
                      <a href="void:javascript(0)" @click="goToDetail(trial.studyname)">
                        {{ trial.studyname }}
                      </a>
                    </h2>
                    <p>{{ trial.protocoltitle.replace('�', '') }}</p>
                    <p>Available in {{ trial.studyentitylocation.split('`').length -1 }} location(s)</p>
                  </div>
                </div>
              </div>
            </li>
          </ul>
        </main>
        <aside role="complementary" class="column is-4-tablet is-3-widescreen">
          <p class="title is-4">Filter Options</p>
          <button class="accordion">Locations</button>
          <div class="panel">
            <form>
              <div class="control">
                <label class="radio">
                  <input type="radio" name="states" value="All" v-model="checkedLocations">
                  All
                </label>
                <label class="radio" v-for="(state, i) in states" :key="i">
                  <input type="radio" name="states" :value="state" v-model="checkedLocations">
                  {{ state }}
                </label>
              </div>
            </form>
          </div>
          <button class="accordion">Phase</button>
          <div class="panel">
            <form>
              <div class="control">
                <label class="radio">
                  <input type="radio" name="phases" value="All" v-model="checkedPhases">
                  All
                </label>
                <label class="radio" v-for="(phase, i) in phases" :key="i">
                  <input type="radio" name="phases" :value="phase" v-model="checkedPhases">
                  Phase {{ phase }}
                </label>
              </div>
            </form>
          </div>
        </aside>
      </div>
    </div>
  </div>
</template>

<script>
import Banner from '@/components/Banner'

export default {
  name: 'Results',
  components: {
    Banner
  },
  data () {
    return {
      checkedLocations: 'All',
      checkedPhases: 'All'
    }
  },
  mounted () {
    this.activateAccordion()
  },
  computed: {
    results () {
      return this.$store.state.results
    },
    states () {
      let statesArray = []
      this.results.forEach((result) => {
        if (result.state) {
          var state = result.state

          state.forEach((item) => {
            if (statesArray.indexOf(item) === -1) {
              statesArray.push(item)
            }
          })
        }
      })
      return statesArray.sort()
    },
    phases () {
      let phaseArray = []
      this.results.forEach((result) => {
        if (result.phasename) {
          var phase = result.phasename

          phase.forEach((item) => {
            if (phaseArray.indexOf(item) === -1) {
              phaseArray.push(item)
            }
          })
        }
      })
      return phaseArray.sort()
    },
    filteredResults () {
      let results = ''
      if (this.checkedLocations !== 'All') {
        results = this.results.filter((result) => result.state.includes(this.checkedLocations))
        return results
      } else {
        return this.results
      }
    }
  }
}
</script>

Here is what the app looks like on the front end Trials App

I'm also new to the modern JavaScript syntax so please be nice lol.

0

2 Answers 2

0

If i understand you, it Seems like you can just add another filter according to the checkedPhases, similar to the filter you already have?

Sign up to request clarification or add additional context in comments.

4 Comments

That was my initial thought and I attempted that but I could have done it wrong. I also need to check for checkedPhases not being 'All' as well. So would it be something like this? if (this.checkedLocations !== 'All' || this.checkedPhases !== 'All') { results = this.results.filter((result) => result.state.includes(this.checkedLocations)) return results } else { return this.results }
But you didn't add the second filter. I thought about: results = this.results.filter(result => checkedLocations !== 'ALL' ? result.state.includes(this.checkedLocations) : result).filter(res => checkedPhases !== 'ALL' ? (YOUR SECOND FILTER) : res)
Sorry I was trying to add the code and then it posted the comment by accident and then I got pulled away for a bit. I'll test that out.
Hey Ravid, that didn't seem to work. I'm able to filter by location but when I try to filter with both the location and phase it only filters on the locations option.
0

I have created a codepen to try to show how you can achieve it. There is a list of items containing carID and cityID. Also, there are two selects to filter the result. When you change a select, it will filter the options of the other select as well. I hope it can help you, if you have any question, just ask.

const data = [
  {
   cityID: 1,
   carID:1,
   name: 'Ted'
  },
  {
   cityID: 1,
   carID:2,
   name: 'Tod'
  },
  {
   cityID: 2,
   carID:1,
   name: 'Michel'
  },
 {
   cityID: 3,
   carID:1,
   name: 'Romeu'
  },
  {
   cityID: 2,
   carID:3,
   name: 'Thomas'
  },
  {
   cityID: 3,
   carID:4,
   name: 'Lucy'
  },
  {
   cityID: 4,
   carID:1,
   name: 'Mary'
  },
]

const cities = [{ cityID: 1, name:'New York'},{ cityID: 2, name:'Sydney'}, { cityID: 3, name:'Chicago'},{ cityID: 4, name:'Perth'}]
const cars = [{ carID: 1, name:'Cruze'},{ carID: 2, name:'Mustang'}, { carID: 3, name:'Blazer'},{ carID: 4, name:'Tucson'}]

new Vue({
  el: '#app',
  data: function() {
    return {
      data,
      cities,
      cars,
      city: "",
      car: "",
    }
  },
  methods: {
    findCarName: function (carID) {
      return this.cars.find(car => car.carID === carID).name
    },
    findCityName: function (cityID) {
      return this.cities.find(city => city.cityID === cityID).name
    },
    reset: function () {
      this.city = ""
      this.car = ""
    }
    
  }, 
  computed: {
    filteredData: function() {
      let resultData = this.data
      if (this.city) {
        resultData = resultData.filter(item => item.cityID === this.city)
      }
      if (this.car) {
        resultData = resultData.filter(item => item.carID === this.car)
      }
      return resultData
    },
    
    filteredCars: function () {
      const carIDs = this.filteredData.reduce((acc, next) => {
        if (acc.indexOf(next.carID) === -1){
          return [...acc, next.carID]  
        }
        return acc
      },[])
      
      if (carIDs.length) {
        return carIDs.map(carID => ({carID, name: this.findCarName(carID)}))
      }
      return this.cars
    },
    
    filteredCities: function () {
      const citiesIDs = this.filteredData.reduce((acc, next) => {
        if (acc.indexOf(next.cityID) === -1){
          return [...acc, next.cityID]  
        }
        return acc
      },[])
      
      if (citiesIDs.length) {
        return citiesIDs.map(cityID => ({cityID, name: this.findCityName(cityID)}))
      }
      return this.cities
    }
  }
  
})
#app {
  margin: 30px;
}
#app .form-group {
  display: flex;
  align-items: center;
}
#app .form-group label {
  font-weight: bold;
  color: #337ab7;
  margin-right: 20px;
}
#app .filters {
  margin-bottom: 20px;
  display: flex;
  width: 700px;
  justify-content: space-around;
}
#app .table {
  width: 700px;
}
#app .table thead tr td {
  font-weight: bold;
  color: #337ab7;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <div class="filters row">
    <div class="form-group">
       <label for="city">City</label>
        <select v-model="city" id="city" class="form-control">
          <option value="">Select City</option>
          <option v-for="item in filteredCities" :key="item.cityID" :value="item.cityID">{{item.name}}</option>
         </select>
      
    </div>  
    <div class="form-group">
      <label for="car">Car</label>    
      <select v-model="car" id="car" class="form-control">
      <option value="">Select Car</option>
      <option v-for="item in filteredCars" :key="item.carID" :value="item.carID">{{item.name}}</option>
      </select>
    </div>
    <div class="form-group">
      <button type="button" class="btn btn-primary" @click="reset">Reset</button>
    </div>
    
  
  </div>
  <table class="table table-striped table-bordered">
    <thead>
      <tr><td>Name</td><td>Car</td><td>City</td></tr>
    </thead>
    <tbody>
      <tr v-for="item in filteredData" :key="data.name">
        <td>{{item.name}}</td>
        <td>{{findCarName(item.carID)}}</td>
        <td>{{findCityName(item.cityID)}}</td>
      </tr>
    </tbody>
  </table>
<div>
  
  
  

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.