I’m figuring out how my search and filter features can work properly.
I created a search feature and filter from search results by stock, distance, price, and time response. My search feature is running well. However, the filter feature that I made still doesn’t work.
I want after I do a search, and want to filter the search further, there are options like stock and distance when one of the dropdowns changes, the search result item also changes based on the filter (for example: stock) is available or not. And there are also other filters such as price and time response. When I click on price, the items will be sorted according to the lowest price to the highest price. And when I click the time response, the search items will be in order from fast to late response.
Example Case
In Type, i choose BMW. And then i click search button. There will show 2 item results with type BMW. And then, in a filter Distance i choose 500 KM. It should, show only 1 result. But the filters not work.
And if there are 6 item result, and i click price the item will be sorted from the lowest price to the highest price
I have made the code like the one below, can anyone help me solve this problem?
new Vue({
el: '#app',
data: {
selectedType: '',
selectedCountry: '',
selectedYear: '',
selectedStock:'',
selectedDistance:'',
items: [{
name: 'Carthy',
type: 'mercedes',
year: '2020',
country: 'england',
stock: 'available',
distance: '500',
price: '1900',
response: 'fast'
},
{
name: 'Holand',
type: 'mercedes',
year: '2020',
country: 'england',
stock: 'available',
distance: '500',
price: '1050',
response: 'fast'
},
{
name: 'Nolan',
type: 'mercedes',
year: '2020',
country: 'england',
stock: 'available',
distance: '500',
price: '1000',
response: 'fast'
},
{
name: 'Edgar',
type: 'bmw',
year: '2020',
country: 'belgium',
stock: 'available',
distance: '5000',
price: '1200',
response: 'fast'
},
{
name: 'John',
type: 'bmw',
year: '2019',
country: 'england',
stock: 'available',
distance: '500',
price: '1500',
response: 'fast'
},
{
name: 'Axel',
type: 'mercedes',
year: '2020',
country: 'england',
stock: 'sold',
distance: '500',
price: '1600',
response: 'late'
}
],
searchResult: [],
itemsToShow: 2,
totalItems: 0,
sortByPrice: true,
sort: 'price',
sortByTime: true,
sort: 'time'
},
computed:{
filterItem: function() {
let filterStock = this.selectedStock,
filterDistance = this.selectedDistance
return this.searchResult.filter(function(item) {
let filtered = true
if (filterStock && filterStock.length > 0) {
filtered = item.stock == filterStock
}
if (filtered) {
if (filterDistance && filterDistance.length > 0) {
filtered = item.distance == filterDistance
}
}
return filtered
})
}
},
methods: {
search: function() {
let filterType = this.selectedType,
filterCountry = this.selectedCountry,
filterYear = this.selectedYear
this.itemsToShow = 2;
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
})
},
priceSort: function(){
this.sortByPrice = !this.sortByPrice
if(this.sortByPrice)
this.sort = 'price'
},
timeSort: function(){
this.sortByTime = !this.sortByTime
if(this.sortByTime)
this.sort = 'time'
}
},
mounted() {
this.search()
}
}).list-item{
margin-top:50px;
}
#app{
position:relative;
padding-bottom: 200px;
}
span{
margin: 0 15px;
cursor:pointer;
}
.filter-box{
margin-top:15px;
}
.card{
box-shadow:0px 10px 16px rgba(0,0,0,0.16);
width:400px;
padding:20px 30px;
margin-bottom:30px;
}
button{
background-color: #1cf478;
border:none;
padding: 10px 25px;
font-weight:bold;
border-radius: 15px;
}
select{
border:none;
padding: 10px 15px;
background-color:#c1c1c1;
border-radius:10px;
}<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">
<div class="search-box">
<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>
<div class="filter-box">
<h6>Filter:</h6>
<select v-model="selectedStock" @change="filterItem">
<option value="" disabled selected hidden>Stock</option>
<option value="sold">Sold</option>
<option value="available">Available</option>
</select>
<select v-model="selectedDistance" @change="filterItem">
<option value="" disabled selected hidden>Kilometers</option>
<option value="500">500 KM</option>
<option value="5000">5000 KM</option>
<option value="10000">10.000 KM</option>
</select>
<span class="price" @click="priceSort">Price</span>
<span class="response" @click="timeSort">Time Response</span>
</div>
<section class="result">
<div class="container-fluid">
<div class="row list-item" v-for="(item, id) in searchResult" :key="id">
<div class="col-3 card" v-if="id < itemsToShow">
<p>Name: {{ item.name }}</p>
<p>Car: {{ item.type }}</p>
<p>Year: {{ item.year }}</p>
<p>Country: {{ item.country }}</p>
<p>Price: ${{ item.price }}</p>
<p>stock: {{ item.stock }}</p>
<p>distance: {{ item.distance }}</p>
</div>
</div>
<div class="row">
<div class="col-12">
<button @click="itemsToShow += 1">Load More</button>
</div>
</div>
</div>
</section>
</div>Advertisement
Answer
Please refer to this codesandbox for a working demo based on your code.
My changes
- Add
filterResultindataon top ofsearchResultto handle filter results, and we only renderfilterResultas results. This would separate the logic of ‘search’ and ‘filter’, clearer for the logic. - Whenever the filter selected value changes, we trigger
filterItemsmethods as@clickhandler. (previously you’re using computed property and that’s not straightforward enough, we can simply call a method for@changeand update thefilter resultsdata. - Whenever the search is triggered, we reset the filter select values, and show all search results (
filterResultwould be the same assearchResultas there is no filter value). - As for sort, for the two sort
spanelements, we could only updatethis.sortdata, and usewatchto triggersortItemsmethod whenever the sorting criteria change, it would be simpler. You may need to adaptsortItemsmethod based on your needs. Of course, you can write two separate methods for these two ‘sort’ buttons, that’s totally up to you 🙂