Vuetify Autocomplete minimum character before filtering - vue.js

Is there a property or a method that will prevent Vuetify Autocomplete to filter items to display until a certain condition is met, such as 3 character typed? I have a basic solution but I really hope that there is another solution. I don't want anything to show until the end user types a minimum of three characters. I have a solutions such as:
watch: {
search (val) {
if(val.length > 2){
this.minimumCharacter = 'show'
}else{
this.minimumCharacter = 'null'
}
And in my HTML:
<template
v-if="minimumCharacter === 'show'"
slot="item"
slot-scope="{ item, tile }"
>
Surely the Autocomplete has a property somewhere that will handle this. When you have thousands and thousands of records you don't really want everything to show as soon as you type one character. But I've search https://vuetifyjs.com/en/components/autocompletes#autocomplete and unless they call it something that I can not relate its not there.

Surely the Autocomplete has a property somewhere that will handle this. When you have thousands and thousands of records you don't really want everything to show as soon as you type one character. But I've search https://vuetifyjs.com/en/components/autocompletes#autocomplete and unless they call it something that I can not relate its not there.
I cannot find such property, but for me works fine this variant:
watch: {
search (val) {
if(val.length > 2){
//search code
}
P.S. Filter starts working after search, so it doesn't solve current task to prevent search.

You can use filter prop to implement your own filter function that always returns false if text length is less then 3:
(item, queryText, itemText) => {
const hasValue = val => val != null ? val : ''
const text = hasValue(itemText)
const query = hasValue(queryText)
if(queryText < 3) return false;
return text.toString()
.toLowerCase()
.indexOf(query.toString().toLowerCase()) > -1
}

Related

Omit / hide column from filter in KoGrid?

Does anyone know if it is possible to omit / hide a column from the filter (list of checkboxes) on KoGrid? If so, how? (I'm hoping there's something that can be done to achieve this in the ColumnDefs property)
(Answering own question, in case it helps others). What I ended up doing, is subscribing to the Grid's showMenu() observable, and hiding elements that pertained to columns with labels that were empty string or only whitespace.
self.Grid().showMenu.subscribe(function (val) {
if (val != true) return;
var colDefId = 0;
self.gridOptions.columnDefs.forEach(function (colDef) {
if (!colDef) return;
if (!/\S/.test(colDef.displayName)) $($('.kgColListItem')[colDefId]).hide();
colDefId++;
});
});

Space character doesn't get recognized in lodash's debounce method with b-taginput in buefy?

I am using buefy's b-taginput with lodash's debounce method to fetch data from an api source during the #typing event. The issue is when I hit spacebar in the input field , inside the debounce method the input character is not recognized as an actual character.
<b-field label="Roles">
<b-taginput
:value="this.objectData.roles"
:data="filteredTags"
autocomplete
field="role"
icon="label"
placeholder="add role..."
#focus="getAsyncRole"
#typing="getAsyncRole"
#input="(newValue) => {updateValue(newValue, 'roles')}"
>
<template slot-scope="props">
<p>{{props.option.role}}</p>
</template>
<template slot="empty">There are no items</template>
</b-taginput>
</b-field>
getAsyncRole: debounce(function(name) {
console.log('inside getAsyncRole and name.length is '+name.length) // the length is 0 when i hit
spacebar but why?
if (!name.length) {
this.filteredTags = [];
return; //exits the function if length of input is zero
}
this.isFetching = true;
api
.getSearchData(this.sessionData.key,`/role/?filter={role} LIKE '%25${name}%25'`)
.then(response => {
console.log('response for getasync role is'+JSON.stringify(response))
this.filteredTags = [];
response.forEach(item => {
this.filteredTags.push(item);
});
})
.catch(error => {
this.filteredTags = [];
throw error;
})
.finally(() => {
this.isFetching = false;
});
}, 500),
The above mentioned code works if I type any alphabetic character (i.e. it give's me the possible autocomplete results based on input character). But I also want it to list out all the autocomplete results (total results) when I hit spacebar into the b-taginput. Since it doesn't recognize the space character as an actual character, name.length become zero, and then it exits the function without making the api call.
NOTE: I noticed that this issue occurs only for b-taginput. This issue does not occur in the case of <b-autocomplete>. With <b-autocomplete> if I hit spacebar then I get all the results as desired. Therefore, I think this issue is specific only to b-taginput. Please help by advising a workaround for this.
The source code indicates that #typing trims the input before emitting it. This leaves a couple options, the best one (by far) is to pre-fetch the unfiltered list. With the list in hand, you can filter exactly as the example code does, searching for the input string within the list.
(The example code works because the empty string '' emitted by typing a space is "found" in every string)
Think about this: you're debouncing the API because you're concerned about hitting it too hard. Drop the debounce and just hit it once. Worried that fetching all tags is too long to wait? Just wait once and never wait again (consider that you were willing to incur this wait on every blank input).

Vue + Vue-Paginate: Array will not refresh once empty

I am using vue-paginate in my app and I've noticed that once my array is empty, refreshing its value to an array with contents does not display.
<paginate
name="recipes"
:list="recipes"
:per="16"
class="p-0"
>
<transition-group name="zoom">
<div v-for="recipe in paginated('recipes')" :key="recipe.id">
<recipe class=""
:recipe="recipe"
:ref="recipe.id"
></recipe>
</div>
</transition-group>
</paginate>
This is how things get displayed, and my recipe array changes depending on a search. If I type in "b" into my search, results for banana, and bbq would show. If I typed "ba" the result for bbq is removed, and once I backspace the search to "b" it would re-appear as expected.
If I type "bx" every result is removed and when I backspace the search to "b", no results re-appear.
Any idea why this might happen?
UPDATE
When I inspect the component in chrome I see:
currentPage:-1
pageItemsCount:"-15-0 of 222"
Even though the list prop is:
list:Array[222]
Paginate needs a key in order to know when to re-render after the collection it's looking at reaches a length of zero. If you add a key to the paginate element, things should function as expected.
<paginate
name="recipes"
:list="recipes"
:per="16"
class="p-0"
:key="recipes ? recipes.length : 0" // You need some key that will update when the filtered result updates
>
See "Filtering the paginated list" is not working on vue-paginate node for a slightly more in depth answer.
I found a hacky workaround that fixed it for my app. First, I added a ref to my <paginate></paginate> component ref="paginator". Then I created a computed property:
emptyArray () {
return store.state.recipes.length == 0
}
then I created a watcher that looks for a change from length == 0 to length != 0:
watch: {
emptyArray: function(newVal, oldVal) {
if ( newVal === false && oldVal === true ) {
setTimeout(() => {
if (this.$refs.paginator) {
this.$refs.paginator.goToPage(page)
}
}, 100)
}
}
}
The timeout was necessary otherwise the component thought there was no page 1.
Using :key in the element has certain bugs. It will not work properly if you have multiple search on the table. In that case input will lose focus by typing single character. Here is the better alternative:
computed:{
searchFilter() {
if(this.search){
//Your Search condition
}
}
},
watch:{
searchFilter(newVal,oldVal){
if ( newVal.length !==0 && oldVal.length ===0 ) {
setTimeout(() => {
if (this.$refs.paginator) {
this.$refs.paginator[0].goToPage(1)
}
}, 50)
}
}
},

React-Native + Redux: Random number of form fields

I am a newbie to react-native, redux and saga and have run into a use case that I have not been able to find a solution for. I understand how to map state to properties and pass around the state between action, reducer and saga. This makes sense to me so far. This is where things seem to get dicey. I have a form that requires a variable number of form fields at any given time depending upon what is returned from the database.
As an example, let's say I have a structure like this:
{
name: ‘’,
vehicleMake: ‘’,
vehicleModel: ‘’,
carLotCity: ‘’,
carLotState: ‘’,
carLotZipCode: ‘’,
localPartsManufacturers: [{name: ‘’, address: ‘’, zipCode}]
}
Everything from name to carLotZipCode would only require one text field, however, the localPartsManufacturers array could represent any number of object that each would need their own set of text fields per each object. How would I account for this with redux as far as mapping the fields to the state and mapping the state to the properties? I am confused about how to begin with this scenario. I understand how to project mapping when the fields are fixed.
I would keep the data as it is coming from the backend. That way you'll avoid normalizing it. I think we just have to be smarter when rendering the fields. Here's what I'm suggesting:
function onTextFieldChange(name, index) {
// either name = `name`, `vehicleMake`, ...
// or
// name = `localPartsManufacturers` and `index` = 0
}
function createTextField(name, index) {
return <input
type='text'
name={ name }
onChange={ () => onTextFieldChange(name, index) } />;
}
function Form({ fields }) {
return (
<div>
{
Object.keys(fields).reduce((allFields, fieldName) => {
const field = fields[fieldName];
if (Array.isArray(field)) {
allFields = allFields.concat(field.map(createTextField));
} else {
allFields.push(createTextField(fieldName));
}
return allFields;
}, [])
}
</div>
);
}
Form receives all the data as you have it in the store. Then we check if the field is an array. If it is an array we loop over the fields inside and generate inputs same as the other properties createTextField. The tricky part here is how to update the data in the store. Notice that we are passing an index when the text field data is changed. In the reducer we have to write something like:
case FIELD_UPDATED:
const { name, index, value } = event.payload;
if (typeof index !== 'undefined') {
state[name][index] = value;
} else {
state[name] = value;
}
return state;
There is nothing preventing you from keeping a list, map, set or any other object in Redux.
The only thing remaining then, is how you map the state to your props, and how you use them. Instead of mapping a single element from the collection to a prop, you map the entire collection to a single prop, and then iterate over the collection in your render method.
In the action you can pass a new collection back, which is comprised of the form fields making up the parts list. Then, your reducer will replace the collection itself.
Or, upon changing an element in the part collection, you can send an action with its id, find it in the collection in the reducer and replace the element that was changed / add the new one / remove the deleted one.

React Native - Saving search terms after delay

I am creating a search toolbar that allows the user to see their most recent searches, using Realm Browser as my database. I save a search whenever the user types in the TextInput component, however, I don't want to add a search term after each key stroke, but only after the user has stopped typing for certain amount of time.
handleOnChange function will update state and only call getResults after 2 seconds
handleOnChange(text) {
this.setState({
searchStr: text
}, () => setTimeout(() => {
this.getResults()
}, 2000))
}
In getResults, I call my addRecentSearch function if certain criteria is met.
getResults() {
let searchTags = []
let searchCalcs = []
let tagNames = this.state.tags.map((tag) => {
return tag.name
})
if (this.state.searchStr.length >= 2 || this.state.tags.length !== 0) {
searchCalcs = Realm.searchCalcs(this.state.searchStr, tagNames)
Realm.addRecentSearch(this.state.searchStr)
}
this.setState({
results: searchCalcs,
tagsForFiltering: searchTags
})
}
So I use setTimeout to allow enough time for my state to get updated when the user types. Then, once the states been updated, I will want to add the search query. However, I'm not getting the results I expected when grabbing the most recent searches.
For example:
Type: "h"
Result: nothing happens as str must be at least 2 characters in length
Type: "he"
Result: meets criteria, and will add "he" as a recent search term.
Arr: ["he"]
Type: "heart" (Note: adding 3 characters in succession)
Result: It seems that even with the timeout function, my getResults function is being called (thus adding the search query for each character I added)
Arr: ["he", "heart", "heart", "heart"]
I want my arr to look like:
arr: ["he", "heart"]
You aren't fully debouncing in your example. You are only delaying everything by 2000ms. You need to create a timer and then reset it every time a change happens (by clearing and starting timer again). In this way, only the final 'delay' takes effect. Make sense?
You are very close to have written your own debounce function, so you can use clearTimeout, or there are some libraries that do it. See Lodash https://lodash.com/docs/#debounce