How to create reactive search input field in Vue.js?

I want to build a seach input field that sorts an object array while typing, using vue 3 with script setup.

input field:

<input type="search" placeholder="Search" v-model="state.search">

script setup:

const state = reactive({
    search: ''
})

const array = [
    {id: 1, title: 'Valhalla', content: '123'},
    {id: 2, title: 'Wurstopia', content: '456'},
    {id: 3, title: 'Brandon', content: '789'}
]

const search  = computed(() => {
    // sort array reactively according to search (use title as sorting criteria)
    const result = sort(array['title'], state.search)
})

Is using computed the right approach for this? How do I reactively sort the array for search input ~ title?

If making this reactive is a problem, I am also happy with an approach of just submitting the input and sorting the array afterwards.

Edit:

I've tried the approach of @AdriHM but it produces exactly the same unsorted array:

const state = reactive({
    search: '',
    array: [
        {id: 1, title: 'Valhalla', content: '123'},
        {id: 2, title: 'Wurstopia', content: '456'},
        {id: 3, title: 'Brandon', content: '789'}
    ]
})

function mySort(searchKey){
    let matchedKeys = [], notMatchedKeys = [];

    for(let i = 0; i < state.array.length; i++) {
        if (state.array[i]['title'].match(searchKey) ) {
            matchedKeys.push(state.array[i])
        } else{
            notMatchedKeys.push(state.array[i])
        }
    }
}

console.log(mySort(state.search))

Output:

(3) [Proxy, Proxy, Proxy]
    0: Proxy {id: 1, title: 'Valhalla', content: '123'}
    1: Proxy {id: 2, title: 'Wurstopia', content: '456'}
    2: Proxy {id: 3, title: 'Brandon', content: '789'}
    length: 3
    [[Prototype]]: Array(0)

Upvotes: 2

Views: 1592

Answers (2)

tony19
tony19

Reputation: 138326

If I understand correctly, the objective is to filter an array by a search term, and display the results sorted by title.

The sorting itself does not need to be reactive because the array is always sorted by title. The array can be sorted once, and then the sorted array can be filtered reactively in the computed prop.

  1. Use Array.prototype.sort() to sort array[] by the title field.

  2. In the computed prop, use Array.prototype.filter() to include only items whose title or content field contains state.search. filter() does not change the order of the results, so no additional sorting is needed.

<script setup>
import { reactive, computed } from 'vue'

const state = reactive({
  search: '',
})

const array = [
  { id: 1, title: 'Valhalla', content: '123' },
  { id: 2, title: 'Wurstopia', content: '456' },
  { id: 3, title: 'Brandon', content: '789' },
]

1️⃣
array.sort((a, b) => a.title.localeCompare(b.title))

const results = computed(() => {
  2️⃣
  return array.filter(item => item.title.includes(state.search) || item.content.includes(state.search))
})
</script>

demo

Upvotes: 0

Adri HM
Adri HM

Reputation: 3070

If what you want to do is a sort you can do it like this:

<template>
  <input type="search" placeholder="Search" v-model="state.search">
  {{ state.array }}
</template>

<script lang="ts" setup>
import {reactive, watch} from "vue";

const state = reactive({
  search: '',
  array: [
    {id: 1, title: 'Valhalla', content: '123'},
    {id: 2, title: 'Wurstopia', content: '456'},
    {id: 3, title: 'Brandon', content: '789'}
  ]
})

function mySort(searchKey: string){
  let matchedKeys = [], notMatchedKeys = [];

  for(let i = 0; i < state.array.length; i++) {
    if (state.array[i]['title'].match(searchKey) ) {
      matchedKeys.push(state.array[i])
    } else{
      notMatchedKeys.push(state.array[i])
    }
  }
  return matchedKeys.concat(notMatchedKeys);
}


watch(() => state.search, () => {
  // sort of filter
  state.array = mySort(state.search)
})
</script>

It will only put at first position the element that match the query but you have the logic to make the array changing with watch.

Upvotes: 1

Related Questions