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 >
<input type="search" v-on:change="filterList" />
{{ search }}
</div>
<div id="listaDestaque" >
<div >
<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 >
<img v-bind:src="item.attributes.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)
CodePudding user response:
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 >
<input type="search" v-model="search"/>
{{ search }}
</div>
<table id="listaDestaque" >
<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>