I have several tracks in an array and have a search and checkboxes to filter the tracks. However, the checkboxes only return the first identified string in each filter. So if I click the checkbox for pop - in the example below - it will only show tracks containing only the pop genre. Tracks containing rock, pop genre won't appear. This is in composition API in Vue.
Here is the script:
const searchTerm = ref('')
const genreFilter = ref([])
const moodsFilter = ref([])
const tempoFilter = ref([])
const themeFilter = ref([])
const filteredTracks = computed(() => {
let filtered = useSong.tracks;
if (searchTerm.value) {
const term = searchTerm.value.toLowerCase();
filtered = filtered.filter(track =>
track.description.toLowerCase().includes(term) ||
track.genre.toLowerCase().includes(term) ||
track.keywords.toLowerCase().includes(term) ||
track.moods.toLowerCase().includes(term)
);
}
if (genreFilter.value.length > 0) {
filtered = filtered.filter(track => genreFilter.value.includes(track.genre))
}
if (moodsFilter.value.length > 0) {
filtered = filtered.filter(track => moodsFilter.value.includes(track.moods))
}
if (tempoFilter.value.length > 0) {
filtered = filtered.filter(track => tempoFilter.value.includes(track.tempo))
}
if (themeFilter.value.length > 0) {
filtered = filtered.filter(track => themeFilter.value.includes(track.theme))
}
return filtered;
});
Here is the html:
<div class="genres w-1/4"><h1 class="font-bold text-[#aeaeae]">Genres</h1><br>
<input class="css-checkbox" type="checkbox" id="Rock" key="genre" value="Rock" v-
model="genreFilter">
<label class="css-label pl-2 text-s font-light text-[#aeaeae]" for="Rock">Rock</label>
<input class="css-checkbox" type="checkbox" id="Pop" key="genre" value="Pop" v-
model="genreFilter">
<label class="css-label pl-2 text-s font-light text-[#aeaeae]" for="Pop">Pop</label>
How can I modify this so that I can check multiple boxes of genres and have them all show, removing only the unchecked boxes. Any help will be greatly appreciated. Also, you'll notice in the filters I have one for moods, tempo and theme. Whenever I click any of those checkboxes, nothing appears at all in the array despite the array having matching moods, tempo, and theme. Thanks.
Your genreFilter
variable is an array:
const genreFilter = ref([])
But you put the whole thing into the checkboxes' v-model
:
<input type="checkbox" value="Rock" v-model="genreFilter">
effectively replacing the array with a string.
So when you do
filtered = filtered.filter(track => genreFilter.value.includes(track.genre))
You are not calling Array.prototype.includes()
, but String.prototype.includes()
. Which works, but not as you intended.
Simple solution is to use indexes in the checkbox:
<input type="checkbox" value="Rock" v-model="genreFilter[0]">
A probably better solution is to add or remove elements when checking the checkbox:
<input type="checkbox" value="Rock" @change="updateGeneres($event.target)">
and
const updateGenres = ({value, checked}) => checked
? (genreFilter.value.includes(value) || genreFilter.value.push(value))
: (genreFilter.value = genreFilter.value.filter(g => g !== value))
const { createApp, ref, computed} = Vue;
const App = {
setup(){
const genres = ['Rock', 'Pop']
const genreFilter = ref([])
const updateGenres = ({value, checked}) => checked
? (genreFilter.value.includes(value) || genreFilter.value.push(value))
: (genreFilter.value = genreFilter.value.filter(g => g !== value))
const items = [
{genre: 'Pop', title: 'This is Pop'},
{genre: 'Rock', title: 'This is Rock'},
{genre: 'Something Else', title: 'This is something else'},
]
const filteredItems = computed(() => genreFilter.value.length === 0 ? items : items.filter(i => genreFilter.value.includes(i.genre) ))
return {genres, genreFilter, updateGenres, filteredItems}
},
}
const app = createApp(App)
app.mount('#app')
<div id="app">
<div v-for="genre in genres">
<label>
<input
type="checkbox"
:value="genre"
@change="updateGenres($event.target)"
> {{ genre }}
</label>
</div>
Selected Genres: {{genreFilter}}
<ul>
<li v-for="item in filteredItems">{{ item.title }}</li>
</ul>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
Here is the snippet in a SFC with setup script