Динамически фильтровать источник данных цикла в Vue3

#vue.js #vuejs3

#vue.js #vuejs3

Вопрос:

Цель
Я хочу отфильтровать источник данных, используемый в цикле for, на основе ввода строки поиска.

Пример
У меня есть источник данных [{ title: «Title1» }, { title: «Title2» }]. Я ввожу «Title1» в свою строку поиска. Я только хочу, чтобы отфильтрованный источник данных for-цикла отображал объект с «Title1».

Проблема
Метод searchInputChange(…), похоже, работает, и источник данных this.filteredPosts, похоже, изменяется правильно. Однако источник данных, похоже, не обновляется в шаблоне HTML. Он всегда содержит оба объекта.

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>
 

Спасибо!

Ответ №1:

При использовании ref внутри setup вам нужно изменить его значение (или прочитать значение) с помощью value свойства, чтобы все присвоения (внутри setup ) filteredPosts должны выглядеть следующим образом filteredPosts.value = ....

За пределами setup (скажем, в methods ), filteredPosts это обычное реактивное свойство реактивного объекта (экземпляра компонента) и .value не требуется (и его использование является ошибкой)

Все это довольно запутанно и является одной из причин, по которой не рекомендуется смешивать Composition API и Options API — просто используйте один или другой.

В вашем случае filteredPosts это может быть просто computed , без необходимости переназначать его вручную каждый раз при изменении входных данных…

 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/vue@3.2.23/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> 

Комментарии:

1. Это работает! Отличный пример. Спасибо.