Home > Net >  Dynamically filter for-loop data source in Vue3
Dynamically filter for-loop data source in Vue3

Time:12-03

Goal
I want to filter a data source used in a for-loop based on the input of a search bar.

Example
I have data source [{ title: "Title1" }, { title: "Title2" }]. I enter "Title1" in my search bar. I only want the filtered data source of the for-loop to show the object with "Title1"

Issue
The searchInputChange(...) method seems to work and the this.filteredPosts data source seems to change correctly. However, the data source does not seem to update in the HTML template. It always contains both objects.

Home.vue

    <template>
        <Searchfield @searchInputChange="searchInputChange" />
    
        <div v-for="post in filteredPosts" :key="post.id">
          {{ post.title }}
        </div>
    </template>
    
    <script>
    import { ref } from 'vue'
    import Searchfield from '../components/Searchfield.vue'
    
    export default {
      name: 'Home',
      components: { Searchfield },
      setup() {
        let filteredPosts = ref([])
        
        let allPosts = [
          {
            "id": 1,
            "title": "Title1",
          },
          {
            "id": 2,
            "title": "Title2",
          }
        ]
      
        filteredPosts.value = [...allPosts]
    
        return { allPosts, filteredPosts }
      },
      methods: {
        searchInputChange(value) {
          const filterPosts = this.allPosts.filter(post => post.title.toLowerCase().includes(value.toLowerCase()));
          this.filteredPosts.value = [...filterPosts]
        }
      }
    }
    </script>

Searchfield.vue

    <template>
        <input type="text" @change="searchInputChange" placeholder="Search...">
    </template>
    
    <script>
    export default {
        name: 'Searchfield',
        setup(props, context) {
            
            const searchInputChange = (event) => {
                context.emit('searchInputChange', event.target.value)
            }
    
            return { searchInputChange }
        }
    }
    </script>

Thanks!

CodePudding user response:

When using ref inside setup you need to change it's value (or read a value) using value property so all assignments (inside setup) to filteredPosts should look like filteredPosts.value = ....

Outside of setup (lets say in methods), the filteredPosts is a normal reactive property of reactive object (component instance) and .value is not needed (and it is an error to use it)

All this is pretty confusing and one of the reasons why mixing Composition API and Options API is not recommended - just use one or the other.

In your case filteredPosts can be a simple computed without the need to manually reassign it every time the input is changed...

const app = Vue.createApp({
  setup(){
    const searchTerm = Vue.ref('')
          
    let allPosts = [
      {
        "id": 1,
        "title": "Title1",
      },
      {
        "id": 2,
        "title": "Title2",
      }
    ]
    
    const filteredPosts = Vue.computed(() => allPosts.filter(post => post.title.toLowerCase().includes(searchTerm.value.toLowerCase())) )
   
    return {
      searchTerm, 
      filteredPosts
    }
  },
})

app.component('Searchfield', {
  props: ['modelValue'],
  emits: ['update:modelValue'],
  setup(props, { emit }) {
    const model = Vue.computed({
      get: () => props.modelValue,
      set: (newValue) => emit('update:modelValue', newValue)
    })
    
    return { model }
  },
  template: `<input type="text" v-model="model" placeholder="Search...">`
})
app.mount('#app')
<script src="https://unpkg.com/[email protected]/dist/vue.global.js"></script>
<div id='app'>
  <searchfield v-model="searchTerm"></searchfield>
 
  <div v-for="post in filteredPosts" :key="post.id">
      {{ post.title }}
    </div>
</div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related