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

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)

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.

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.
Use Array.prototype.sort() to sort array[] by the title field.
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

Related

vue - Bootstrap-vue: Select row (primary-key) based on an object with the same key values

how can I select each rows with the same values from a separate object?
<b-table
:items="CurrentObject"
:select-mode="selectMode"
ref="selectableTable"
selectable
#row-selected="onRowSelected"
primary-key="uniqueID"
>
CurrentObject:
[
{name: 'A', uniqueID: 123, text: 'lorem ipsum'},
{name: 'B', uniqueID: 456, text: 'lorem ipsum'},
{name: 'C', uniqueID: 789, text: 'lorem ipsum'},
]
Separate Object:
[
{uniqueID: 123},
{uniqueID: 456},
{uniqueID: 789},
]
Using JavaScript's array.findIndex() and VueBootstrap's selectRow() seem to do it.
Template:
<template>
<b-container>
<div>
<h1>Current Object</h1>
<b-table
:items="currentObject"
:select-mode="selectMode"
ref="selectableTable"
selectable
#row-selected="onRowSelected"
primary-key="uniqueID"
>
</b-table>
<h2>Separate Object</h2>
<div v-for='object in separateObject' #click='selectMyRow(object.uniqueID);'>{{ object.uniqueID }}</div>
</div>
</b-container>
</template>
Script:
<script lang="ts">
import { Vue } from 'vue-property-decorator';
export default class HelloWorld extends Vue {
selectMode = 'single';
currentObject = [
{name: 'A', uniqueID: 123, text: 'lorem ipsum'},
{name: 'B', uniqueID: 456, text: 'lorem ipsum'},
{name: 'C', uniqueID: 789, text: 'lorem ipsum'},
];
separateObject = [
{uniqueID: 123},
{uniqueID: 456},
{uniqueID: 789},
];
selectMyRow(uniqueID) {
const row = this.currentObject.findIndex(x => x.uniqueID === uniqueID);
this.$refs.selectableTable.selectRow(row);
}
onRowSelected() {
// do something else
}
}
</script>
Working example:
If instead, you need similar functionality using select-mode multi, use the following:
selectMode = 'multi';
...
selectMyRow(uniqueID) {
const row = this.currentObject.findIndex(x => x.uniqueID === uniqueID);
const table = this.$refs.selectableTable
if (table.isRowSelected(row)) {
table.unselectRow(row);
} else {
table.selectRow(row);
}
}
OK, I have the first solution for me.
I couldn't update the jsfiddle yet ...
I have 2 watch functions, each with a for loop
the first loop checks whether in separateArray
var TableStore = this.TableStore; // -> ARRAY NO OBJECT
for (var i = 0; i < TableStore.length; i++) {
const row = this.CurrentArray.findIndex(x => x.unID === TableStore[i]);
this.$refs.selectableTable.selectRow(row);
}
the second checks whether not.
(filter / includes -> CurrentArray - SeparateArray)

Why paginate plugin and skip/limit don't work in that mongodb query?

const paginateScheme = new Schema({
posts: [ { img: String, description: String, liked: String, postComId: String, comments: [ { author: String, text: String } ] } ],
}, {collection: "usersData"});
paginateScheme.plugin(mongoosePaginate);
const myModel = mongoose.model("sample", paginateScheme);
app.put("/api/pagination", function(req, res){
const options = {
page: 1,
limit: 3,
collation: {
locale: 'en',
},
};
const query = User.find({mail: 'user2#example.com'}, {posts: 1 });
myModel.paginate( query , options, function (err, result) {
if(err) return console.log(err);
res.send(result);
});
});
where post array of objects which I want to paginate.
I checked this plugin works correctly. When I use Model.find({}) it paginates through outer objects without any problems. I tried to use skip + limit but it returned nothing.
this query returns:
{
docs: [ { _id: 601a8f013d86dc237468467c, posts: [Array] } ],
totalDocs: 1,
limit: 3,
totalPages: 1,
page: 1,
pagingCounter: 1,
hasPrevPage: false,
hasNextPage: false,
prevPage: null,
nextPage: null
}
you can using $slice for paginate a array like this(use async/await)
let result = await myModel.find({mail: 'user2#example.com'}, {posts: {$slice: [0, 3]}})

Traverse through an Array to obtain nested object

I am trying to traverse through my array to get a specific object nested inside of it.
Some objects contain a children property, which should be traversed until a matching object is found.
Here's some example data, I am trying to obtain the object with id as 4
const items = [{
id: 1,
title: 'Title for Item 1'
},
{
id: 2,
title: 'Title for Item 2',
children: [
{
id: 3,
title: "Title for Item 3",
children: [
{
id: 4,
title: "Title for Item 4",
}
]
}
]
},
]
I've written some traversal code but it returns undefined.
const items = [{
id: 1,
title: 'Title for Item 1'
},
{
id: 2,
title: 'Title for Item 2',
children: [
{
id: 3,
title: "Title for Item 3",
children: [
{
id: 4,
title: "Title for Item 4",
}
]
}
]
},
]
const getItem = (items) => {
if (!items) return;
const item = items && items.find(i => i.id === 4);
if (!item) {
items.forEach(i => {
return getItem(i.children)
})
// This is where undefined is returned
} else {
console.log({
item
}) // Prints the correct object.
return item;
}
};
const s = getItem(items); // undefined
document.querySelector('#foo').textContent = s ? s : 'undefined';
<div id="foo"></div>
At least two issues explain why it does not work:
A return statement in a forEach callback will return the returned value to nowhere. Nothing happens with it.
The result of the recursive call is not checked. It needs to be checked to see if it is defined. Depending on that you can decide whether to continue the loop or exit from it.
Replace that forEach with a for...of loop so you can return "out of it", but only do that when you have a match, otherwise you need to continue the loop:
for (const item of items) {
const match = getItem(item.children);
if (match) return match;
}
Note that in your snippet you should not set the textContent to the return value, as that is an object and will get converted to the string "[Object object]". You could for instance just grab the title string and put that in textContent:
const items = [{
id: 1,
title: 'Title for Item 1'
},
{
id: 2,
title: 'Title for Item 2',
children: [
{
id: 3,
title: "Title for Item 3",
children: [
{
id: 4,
title: "Title for Item 4",
}
]
}
]
},
]
const getItem = (items) => {
if (!items) return;
const item = items && items.find(i => i.id === 4);
if (!item) {
for (const item of items) {
const match = getItem(item.children);
if (match) return match;
}
} else {
console.log({
item
}) // Prints the correct object.
return item;
}
};
const s = getItem(items); // undefined
document.querySelector('#foo').textContent = s ? s.title : 'undefined';
<div id="foo"></div>

Semantic React - Form.Select how to use custom array options

the semantic react docs about From.Select is to give it a prop options that needs to have a specific array like that :
const options = [
{ key: 'm', text: 'Male', value: 'male' },
{ key: 'f', text: 'Female', value: 'female' },
]
and using it like that :
<Form.Field control={Select} label='Gender' options={options} placeholder='Gender' />
if i want to use other array with custom keys and values like that for example :
const options = [
{ date: 'somedate', title: 'sometitle', },
{ date: 'somedate', title: 'sometitle', },
]
i am getting an error about using wrong props
my question is how can i use my own array with this Select Component
thanks !
You can reformat you options before sending it to semantic component:
import { get, map } from 'lodash'
const reformatOptions = options =>
map(options, e => ({
key: get(e, 'date'),
value: get(e, 'date'),
text: get(e, 'title'), // (or whatever other format you wish to use)
}))
And then pass the options=reformatOptions(options) to your semantic component

What is the best way to combine two immutable lists?

I have two lists and i'm trying to combine them to a new list so that the existing ids are updated and the new ones are added to list and after that sorted by the id. Is there a better or more efficient way to do this?
// Original list
const list = Immutable.List([
{ id: 1, name: 'List Item 1' },
{ id: 2, name: 'List Item 2' },
{ id: 3, name: 'List Item 3' },
]);
// One updated item and two new items
const newList = Immutable.List([
{ id: 2, name: 'Updated List Item 2' },
{ id: 4, name: 'New List Item 4' },
{ id: 5, name: 'New List Item 5' },
]);
// Get updated ids
const ids = newList.map((item) => item.id);
// Filter out updated ids from orignial list
const filteredList = list.filterNot(item => ids.includes(item.id));
// Concat and sort by id
const concatList = newList
.concat(filteredList)
.sortBy(item => item.id);
console.log(concatList.toJS());
/* Outputs as desired
[
{ id: 1, name: "List Item 1" },
{ id: 2, name: "Updated List Item 2" },
{ id: 3, name: "List Item 3" },
{ id: 4, name: "New List Item 4" },
{ id: 5, name: "New List Item 5" }
]
*/
This is how I would do it, using reduce and merge:
function reduceToMap(result, item) { return result.set(item.id, item) }
const list = Immutable.List([
{ id: 1, name: 'List Item 1' },
{ id: 2, name: 'List Item 2' },
{ id: 3, name: 'List Item 3' },
]).reduce(reduceToMap, Immutable.Map());
// One updated item and two new items
const newList = Immutable.List([
{ id: 2, name: 'Updated List Item 2' },
{ id: 4, name: 'New List Item 4' },
{ id: 5, name: 'New List Item 5' },
]).reduce(reduceToMap, Immutable.Map());
console.log(...list.merge(newList).values())
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.js"></script>