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
filterResult
indata
on top ofsearchResult
to handle filter results, and we only renderfilterResult
as results. This would separate the logic of ‘search’ and ‘filter’, clearer for the logic. - Whenever the filter selected value changes, we trigger
filterItems
methods as@click
handler. (previously you’re using computed property and that’s not straightforward enough, we can simply call a method for@change
and update thefilter results
data. - Whenever the search is triggered, we reset the filter select values, and show all search results (
filterResult
would be the same assearchResult
as there is no filter value). - As for sort, for the two sort
span
elements, we could only updatethis.sort
data, and usewatch
to triggersortItems
method whenever the sorting criteria change, it would be simpler. You may need to adaptsortItems
method based on your needs. Of course, you can write two separate methods for these two ‘sort’ buttons, that’s totally up to you 🙂