Filter array based on multiple options (Vue/JavaSc

2019-08-21 12:29发布

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.

2条回答
Juvenile、少年°
2楼-- · 2019-08-21 12:51

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>
  
  
  

查看更多
家丑人穷心不美
3楼-- · 2019-08-21 12:54

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

查看更多
登录 后发表回答