Skip to content
Advertisement

Data becoming a proxy when trying to make a SearchBar with vue3

i have an application made with VueJs3, and i trying to make a searchbar based on .filter(), and it seems to be working, but when i try to pass te value from my methods to my template its making a huge error, my data is becoming a proxy, and i cant use the data in a proxy format.

The code:

<template>
  <div class="searchBar">
    <input type="search" v-on:change="filterList" />
    {{ search }}
  </div>
  <div id="listaDestaque" class="list">
    <div class="list-header">
      <p>Imagem</p>
      <p>Descrição</p>
      <p>Categoria</p>
      <p>Un</p>
      <p>Estoque</p>
      <p>Valor</p>
    </div>
    <div v-for="item in current_data">
      <div class="item-list">
        <img v-bind:src="item.attributes.image" class="item-image" />
        <p>{{ item.attributes.name }}</p>
        <p>{{ item.attributes['category-name'].name }}</p>
        <p>{{ item.attributes['unit-name'].name }}</p>
        <p>{{ item.attributes['quantity-in-stock'] }}</p>
        <p>{{ item.attributes.price }}</p>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import api from '../../services/axios.js';
import headers from '../../services/headers.js';

export default defineComponent({
  name: 'listaDestaque',
  data() {
    return { myList: [], search: '', filter: '', current_data: '' };
  },
  beforeMount() {
    api.get('/products/all', headers).then((response) => {
      this.myList = response.data.data;
      const filter = this.myList.filter((element) => element.attributes.highlight == true);
      this.current_data = filter;
    });
  },
  methods: {
    filterList() {
      this.$data.search = event.target.value;
      console.log(this.search);

      const myListArray = JSON.parse(JSON.stringify(this.myList));

      const filtered = myListArray.find(
        (element) => element.attributes.name == this.search
      );

      const filteredArray = JSON.parse(JSON.stringify(filtered));

      if (filtered) {
        this.current_data = filteredArray;
        console.log(this.current_data);
        console.log(filteredArray);
      }
    },
  },
});
</script>

<style scoped></style>

The code refering to the search bar is between the lines 2-5 and 35-65.

The console.log(filteredArray); in line 64 return this:

Proxy {id: '1', type: 'products', attributes: {…}, relationships: {…}}
[[Handler]]: Object
[[Target]]: Object
attributes: {name: 'Small Marble Chair', description: 'Nihil est dignissimos. Quia officia velit. Est aliquid eos.', quantity-in-stock: 12, price: 58.21, highlight: true, …}
id: "1"
relationships: {category: {…}, unit: {…}}
type: "products"
[[Prototype]]: Object
[[IsRevoked]]: false

And i recieve the error in lines 17-22 after i user the function filterList:

listaDestaque.vue:17 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'image')
    at listaDestaque.vue:17:42
    at renderList (runtime-core.esm-bundler.js:2905:26)
    at Proxy._sfc_render (listaDestaque.vue:24:11)
    at renderComponentRoot (runtime-core.esm-bundler.js:896:44)
    at ReactiveEffect.componentUpdateFn [as fn] (runtime-core.esm-bundler.js:5651:34)
    at ReactiveEffect.run (reactivity.esm-bundler.js:185:25)
    at instance.update (runtime-core.esm-bundler.js:5694:56)
    at callWithErrorHandling (runtime-core.esm-bundler.js:155:36)
    at flushJobs (runtime-core.esm-bundler.js:396:17)

Advertisement

Answer

The fact that a value changes to a proxy should not be causing any issues.

The issue is that you are using const filtered = myListArray.find(...). The result of find is either a null or the first object in the array that matches your criteria, so filteredArray will never have an array as the content. When you pass it to the template though, you use for in so the iterator will go through your objects’ properties (ie item at some point will be attributes, so attributes.attributes will be unefined, ergo attributes.attributes.image throws an error.

You probably meant to use filter instead of find

  const filtered = myListArray.filter(
    (element) => element.attributes.name == this.search
  );

I think the solution is a bit overcomplicated here

you can use v-model for the search input and the filtered dataset can use a computed

example: (SFC)

<template>
  <div class="searchBar">
    <input type="search" v-model="search"/>
    {{ search }}
  </div>
  <table id="listaDestaque" class="list">
    <thead>
      <tr>
        <th>Descrição</th>
        <th>Categoria</th>
        <th>Estoque</th>
        <th>Valor</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="item in current_data">
        <td>{{ item.attributes.name }}</td>
        <td>{{ item.attributes['category-name'] }}</td>
        <td>{{ item.attributes['quantity-in-stock'] }}</td>
        <td>{{ item.attributes.price }}</td>
      </tr>
    </tbody>
  </table>
</template>

<script lang="ts">
  import { defineComponent } from 'vue';
  
  const dataset = [
    {attributes:{name:"apple", "category-name":"fruit", "quantity-in-stock": 0.2, price: "$3.98"}},
    {attributes:{name:"pear", "category-name":"fruit", "quantity-in-stock": 2, price: "$1.98"}},
    {attributes:{name:"orange", "category-name":"fruit", "quantity-in-stock": 3, price: "$3.98"}},
    {attributes:{name:"iPhone", "category-name":"not fruit", "quantity-in-stock": 18, price: "$398.29"}},
  ]

  export default defineComponent({
    name: 'listaDestaque',
    data() {
      return { myList: [], search: ''};
    },
    beforeMount() {
      // api mocked
      setTimeout(()=>{
        this.myList = dataset  
      }, 500);
    },
    computed: {
      current_data(){
        return this.myList.filter((element) => element.attributes.name.includes(this.search)) || [];
      }
    },
  });
</script>
User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement