How to create a search filter on Vue.js when you use map - vue.js

I tried to do a search in vue but it didn't work.
I tried to make a computed function and every time if I have a word in the search to filter by that word of the product, but I don't understand why it doesn't work
My products
async getProducts() {
this.$store.dispatch("actions/setLoading", true);
const products = (await ProductService.getProducts()).data.products;
this.dataSource = products.map((product, key) => {
return {
key: key + 1,
image_url: (
<div class="record-img align-center-v">
<img
src={product.image_url}
alt={product.product_name}
width="100"
/>
</div>
),
product_name: (<a href={product.product_url}>{product.product_name}</a>),
price: product.price,
price_with_discount: product.price_with_discount,
categories: product.categories,
sku: product.sku,
quantity: product.quantity,
currency: product.currency,
action: (
<div class="table-actions">
asds
<sdFeatherIcons type="trash-2" size={14} /> Test
</div>
)
}
});
this.$store.dispatch("actions/setLoading", false);
}
my filter function
watch: {
search() {
return this.dataSource.filter(product => product.product_name.toLowerCase().includes(searchText.value.toLowerCase()))
}
}
<a-table
:rowSelection="rowSelection"
:pagination="{ pageSize: 10, showSizeChanger: true }"
:dataSource="dataSource"
:columns="columns"
/>

functions that are watchers behave like methods, not like computed functions.
you can write a computed property -
computed:{
computedDataSource(){
return this.dataSource.filter(product => product.product_name.toLowerCase()
.includes(searchText.value.toLowerCase()))
}
html -
<a-table :rowSelection="rowSelection"
:pagination="{ pageSize: 10, showSizeChanger: true }"
:dataSource="computedDataSource"
:columns="columns"/>

Related

How can I get a specifc selection in select vue.js?

How are you?
I'm studying Vue and I'm stuck on the current task not knowing where to go.
I have a select that when I click I need to show on screen only what corresponds to that selection. For example, when placing the "to do" option in the select, only the tasks with a concluded=false should appear on the screen. I've only gotten this far and I need help to continue. Can you help me? Thanks
This is my App.vue
<template>
<div id="app">
<h1>Lista de Tarefas</h1>
<List :data="list" #remove="handleRemove"/>
<Form #add="addNewTask" #onChange="handleN"/>
</div>
</template>
<script>
import List from "./components/List.vue";
import Form from "./components/Form.vue";
export default {
components: {
List,
Form,
},
data() {
return {
list: [],
};
},
methods: {
addNewTask(newTask) {
this.list.push(newTask);
},
handleRemove(item) {
const index = this.list.findIndex(i => i.id === item.id)
this.list[index].excluded = true
},
handleN(item) {
const index = this.list.findIndex(i => i.id === item.id)
this.list[index].concluded = true
}
},
};
</script>
This is my List.vue
<template>
<ul>
<select v-model="selected" #change="onChange($event)">
<option disabled value="">Escolha a visualização</option>
<option v-for="option in options" :key="option.text">
{{ option.text }}
</option>
</select>
<li v-for="item in itens" :key="item.id">
<input type="checkbox" id="checkbox" v-model="item.concluded" />
<label for="checkbox"> {{ item.description }} </label>
<button #click="() => $emit('remove', item)">Excluir</button>
</li>
</ul>
</template>
<script>
export default {
props: {
data: {
type: Array,
default: () => {},
},
},
data() {
return {
selected: "",
options: [
{ text: "Todos", value: "1" },
{ text: "A fazer", value: "2" },
{ text: "Concluído", value: "3" },
{ text: "Deletado", value: "4" },
],
};
},
computed: {
itens() {
return this.data.filter((item) => item.excluded === false);
},
},
methods: {
onChange(event) {
console.log(event.target.value);
return this.data.filter((item) => item.concluded === false);
},
},
};
</script>
This is my Form.vue
<template>
<form #submit.prevent="handleNewTask">
<input type="text" v-model="newTask" placeholder="Insira a tarefa"/>
<input type="submit" value="Adicionar"/>
</form>
</template>
<script>
import Task from '../types/Task.js'
export default {
data() {
return {
newTask: "",
};
},
methods: {
handleNewTask() {
this.$emit('add', new Task(this.newTask))
this.newTask = ''
}
},
};
</script>
And this is my Task.js
export default class {
constructor(description) {
this.description = description,
this.id = Math.random(),
this.concluded = false,
this.excluded = false
}
}
I watch some tutorials, read the documentation and some StackOverflow questions but I really can't get out of here
Thanks in advance for the help
Based on how you have structured your app, our only concern should be with the List.vue file.
Your goal is to filter the results based on the selection (selected property). However, your issue is that you are not even using that anywhere.
I know you are hard coding the filter on the onChange method but that is, first of all wrong because you aren't really changing anything (you are returning an array), and secondly it's inefficient.
A better way to do it is to update the computed itens function like so:
itens() {
return this.data.filter((item) => {
if (this.selected === '1'){
return item.concluded === false
} else if (this.selected === '2'){
// filter another way
} else if (... // so on and so forth
});
},
Also, I would filter out the excluded items before sending them to the component. If you aren't going to use it, don't send it.
Remove the onChange event on the <select> and the associated method since they are now unused.

vue js append params to URL

I am new to vuejs and I am making an app which should filters all the data from database and filter parameters should be passed in URL, so that it remain in the page even after page refresh.
Firstly, I want to push search params in URL
My current Url localhost:8080/product
I want my Url to be like below when user click checkboxes
localhost:8080/product?color=red&color=green&size=small (When user checks red, green and small options)
So far, I have done this and I am stuck how to get dynamic color in $this.router.push(), and append it to URL
<template>
<div class="products">
<h1>Filter By Color</h1>
<input #click="filterData" v-model="colortype" type="checkbox">Red color
<input #click="filterData" v-model="colortype" type="checkbox">Green color
<input #click="filterData" v-model="colortype" type="checkbox">Yellow color
<h1>Filter By Size</h1>
<input #click="filterData" v-model="size" type="radio">Big
<input #click="filterData" v-model="size" type="radio">Small
</div>
</template>
<script>
export default {
data() {
return {
colortype:''
}
},
methods:{
filterData() {
this.$router.push({ path: "product", query: { color: "red" } });
}
}
}
</script>
Any suggestions, once I push params to URL, I want to do api request to endpoint in filterData method.
There is a pattern you can use for this:
Store the filter values on an object
Deeply watch the filter object and react to it by fetching data / updating url.
The following snippets demonstrate a dropdown where you can pick one value to filter on.
<template>
<div>
Color:
<select v-model="filter.color">
<option v-for="item in colors" :key="item.value" :value="item.value">
{{ item.name }}
</option>
</select>
</div>
</template>
export default {
name: "MyComponent",
data: () => ({
filter: {
color: null,
},
colors: [{name: 'Black', value: 'black'}, {name: 'Red', value: 'red'}],
}),
watch: {
'filter': {
deep: true,
handler(filter) {
this.$router.replace({
...this.$route,
query: {
color: filter.color.value,
// TODO: Convert `filter` to params
},
});
this.search(filter);
},
},
},
};
For the case where there a field accepts multiple values (e.g. using checkboxes) I'd suggest to put all values on the filter object and have each option mark its checked-ness with a flag. The following snippets demonstrate that technique:
<template>
<div>
Sizes:
<label v-for="size in sizes" :key="size.value">
<input type="checkbox" v-model="size.active">
{{ size.name }}
</label>
</div>
</template>
export default {
data: () => ({
filter: {
sizes: [{name: 'Small', value: 'small', active: false}],
},
}),
watch: {
filter: {
deep: true,
handler() {
this.$router.replace({
...this.$route,
params: {
sizes: this.filter.sizes
.filter(size => size.active)
.map(size => size.value)
.join('+'),
},
});
},
},
},
}

Clearing Vue JS v-for Select Field

I have a simple application that uses a v-for in a select statement that generates two select tags. The groupedSKUAttributes variable that creates the select statement looks like this:
groupedSKUAttributes = {colour: [{id: 1, name: 'colour', value: 'red'},
{id: 2, name: 'colour', value: 'blue'}],
size: [{id: 3, name: 'size', value: '40'},
{id: 4, name: 'size', value: '42'}]}
I also have a button that I want to clear the select fields. How do I get the clear method to make each of the select fields choose their default <option value='null' selected>select a {{ attributeName }}</option> value? I can't figure out if I'm meant to use a v-model here for the groupedSKUAttributes. Any advice would be appreciated.
The template looks like this:
<template>
<div>
<select
v-for='(attribute, attributeName) in groupedSKUAttributes'
:key='attribute'
#change='update(attributeName, $event.target.value)'>
<option value='null' selected>select a {{ attributeName }}</option>
<option
v-for='a in attribute'
:value='a.id'
:label='a.value'
:key='a.id'>
</option>
</select>
</div>
<button #click='clear'>clear</button>
</template>
And the JS script looks like this:
<script>
export default {
name: 'app',
data() {
return {
groupedSKUAttributes: null,
}
},
methods: {
clear() {
console.log('clear');
},
update(attributeName, attributeValue) {
console.log(attributeName, attributeValue);
},
getSKUAttributes() {
API
.get('/sku_attribute/get')
.then((res) => {
this.skuAttributes = res.data;
this.groupedSKUAttributes = this.groupBy(this.skuAttributes, 'name');
})
.catch((error) => {
console.error(error);
});
},
},
created() {
this.getSKUAttributes();
}
}
</script>
The v-model directive works within the v-for without any issues.
<script>
export default {
name: 'app',
data() {
return {
groupedSKUAttributes: null,
selected: {}
}
},
methods: {
clear() {
this.generateDefaultSelected(this.generateDefaultSelected);
},
update(attributeName, attributeValue) {
this.selected[attributeName] = attributeValue;
},
getSKUAttributes() {
API
.get('/sku_attribute/get')
.then((res) => {
this.skuAttributes = res.data;
this.groupedSKUAttributes = this.groupBy(this.skuAttributes, 'name');
// Call this method to reset v-model
this.generateDefaultSelected(this.groupedSKUAttributes);
})
.catch((error) => {
console.error(error);
});
},
generateDefaultSelected(groupedSKUAttributes) {
// Reset the object that maintains the v-model reference;
this.selected = {};
Object.keys(groupedSKUAttributes).forEach((name) => {
// Or, set it to the default value, you need to select
this.selected[name] = '';
});
}
},
created() {
this.getSKUAttributes();
}
}
</script>
In the above code, generateDefaultSelected method resets the selected object that maintains the v-model for all your selects.
In the template, you can use v-model or unidirectional value/#change pair:
<!-- Using v-model -->
<select
v-for='(attribute, attributeName) in groupedSKUAttributes'
:key='attributeName' v-model="selected[attributeName]">
<!-- Unidirection flow without v-model -->
<select
v-for='(attribute, attributeName) in groupedSKUAttributes'
:key='attributeName' :value="selected[attributeName]"
#change='update(attributeName, $event.target.value)'>

Multiple select on the edit form

I have the following form:
This is the edit form.
As you can see I have a multiple select. I need to bind the values from the server to the select.
Here is structure of my objects from the server.
1) All elements for the multiple select:
2) Particular objects, that I want to see selected. As you can see, there's an additional field called 'pivot'.
As a result, I would like to see the following when I open my form:
I have tried something like this, but without success:
<div class="form-group">
<label for="bk">Связанные бк</label>
<select class="form-control form-control-sm" id="bk" v-model="formFields.applicationBk" multiple>
<option v-for="bk in allBk" v-if="applicationBk.find(x => x.id === bk.id) 'selected'" >
{{ bk.name }}
</option>
</select>
Here is full js code:
<script>
import { EventBus } from '../../app';
export default {
name: "ApplicationEdit",
props: ['applicationId', 'name', 'offer', 'bundleId', 'isBlackMode', 'applicationBk', 'allBk'],
mounted: function(){
console.log(this.applicationBk)
},
methods:{
submit: function (e) {
window.axios.post('/application/edit/' + this.applicationId, this.formFields)
.then(res => {
console.log('Сохранил!');
$('#applicationEdit' + this.applicationId).modal('hide');
EventBus.$emit('reloadApplicationsTable');
}).catch(err => {
if(err.response.status === 422){
this.errors = err.response.data.errors || [];
}
//console.error('Ошибка сохранения приложения. Описание: ');
console.error(err)
});
}
},
data(){
return {
formFields: {
applicationId: this.applicationId,
applicationBk: this.applicationBk,
name: this.name,
offer: this.offer,
bundle_id: this.bundleId,
giraffe: this.isBlackMode,
bk: this.applicationBk,
},
errors: []
}
}
}
You might consider using your loop as you have but using v-model to an array of the selected values. Here is vue's example of this: https://v2.vuejs.org/v2/guide/forms.html#Select

multiple v-for on one line

EDIT 1
Here is the script
<script>
import firebase from '#/middleware/firebase'
const database = firebase.database()
export default {
data: function () {
return {
cruises: [],
search: '',
paginate: ['cruises']
}
},
mounted () {
database.ref('cruises').on('child_added', snapshot => this.cruises.push(snapshot.val()))
},
computed: {
filteredCruises: function () {
var self = this;
return this.cruises.filter(function(cruise) {
return cruise.title.toLowerCase().indexOf(self.search.toLowerCase()) >=0;
})
}
}
}
</script>
I'm trying to paginate the filteredCruises I am using the https://github.com/TahaSh/vue-paginate package for the pagination. So I'm just trying to merge the two if I can.
Original Question
Just a quick v-for query.
Is it possible to merge
<section class="shadow p-4 h-32 mb-4" v-for="cruise in paginated('cruise')" :key="cruise.id">
<section class="shadow p-4 h-32 mb-4" v-for="cruise in filteredCruises" :key="cruise.id">
onto one line one because I need to call the paginated as well as a computed function.
Any of these:
<!-- ES6 array spread syntax -->
<section v-for="cruise in [...paginated('cruise'), ...filteredCruises]">
<!-- ES5 -->
<section v-for="cruise in paginated('cruise').concat(filteredCruises)">
You can put it into a computed property if you don't want to litter your markup with too much code.
I don't know if you are trying to paginate the filtered cruises, or filter the paginated cruises, but solution is simple: use one v-for. With computed property which is doing what you need.
Edit: Improved example
<script>
import firebase from '#/middleware/firebase'
export default {
data: _ => ({
cruises: [],
search: '',
paginate: ['cruises'],
db: firebase.database(),
ref: this.db.ref('cruises'),
query: this.ref.on('child_added', this.updateCruises)
}),
computed: {
filteredCruises () {
return this.cruises.filter({title} => {
return new RegExp(this.search, 'i').test(title)
})
}
},
methods: {
updateCruises (snapshot) {
this.cruises.push(snapshot.val())
}
}
}
</script>
<input v-model="search">
<paginate
name="cruises"
:list="filteredCruises"
:per="5"
>
<section v-for="cruise in paginated('cruises')">
{{ cruise.title }}
</section>
</paginate>