Reputation: 329
Currently i am using buefy autocomplete.But there are a few issues with it.
DepartmentDetail.vue
<template slot-scope="props">
<div class="container is-fluid">
<b-loading :is-full-page="true" :active.sync="this.isLoading"></b-loading>
<b-field label="Business Unit">
<b-autocomplete
:data="dataBusinessUnit"
placeholder="select a business unit..."
field="businessUnit"
:loading="isFetching"
:value="this.objectData.businessUnit"
@typing="getAsyncDataBusinessUnit"
@select="(option) => {updateValue(option.id,'businessUnit')}"
>
<template slot-scope="props">
<div class="container">
<p>
<b>ID:</b>
{{props.option.id}}
</p>
<p>
<b>Description:</b>
{{props.option.description}}
</p>
</div>
</template>
<template slot="empty">No results found</template>
</b-autocomplete>
</b-field>
</div>
</template>
Function that fetches the results based on user input-
getAsyncDataBusinessUnit: debounce(function(name) {
// console.log('getAsyncDataBusinessUnit you typed'+name);
if (!name.length) {
this.dataBusinessUnit = [];
return;
}
this.isFetching = true;
api
.getSearchData(this.sessionData.key,`/businessunit/${name}`)
.then(response => {
this.dataBusinessUnit = [];
response.forEach(item => {
this.dataBusinessUnit.push(item);
});
})
.catch(error => {
this.dataBusinessUnit = [];
throw error;
})
.finally(() => {
this.isFetching = false;
});
}, 500),
So instead of using the buefy's b-autocomplete i want to create and use my own autocomplete component. So i went ahead and created my own autocomplete component and called it from the DepartmentDetail vue page like this-
DepartmentDetail.vue
<b-field label="Custom Business Unit ">
<AutoComplete :method="getAsyncDataBusinessUnit" title='businessUnit' :autocompleteData="dataBusinessUnit" viewname='DepartmentDetail'>
</AutoComplete>
</b-field>
AutoComplete.vue
<template>
<div class="autocomplete">
<input style="font-size: 12pt; height: 36px; width:1800px; " type="text" v-model="this.objectData[this.title]" @input="getAsyncDataBusinessUnit"/>
<ul v-show="isFetching" >
<li v-for="(dataBusinessUnit, i) in dataBusinessUnit" :key="i" @click="setResult(dataBusinessUnit)" >
<!-- {{ autocompleteData }} -->
<template v-if="title!='manager'">
<div class="container">
<p>
<b>ID:</b>
{{dataBusinessUnit.id}}
</p>
<p>
<b>Description:</b>
{{dataBusinessUnit.description}}
</p>
</div>
</template>
<template v-else>
<div class="container">
<p>
<b>ID:</b>
{{dataBusinessUnit.id}}
</p>
<p>
<b>First Name:</b>
{{dataBusinessUnit.firstName}}
</p>
<p>
<b>Last Name:</b>
{{dataBusinessUnit.lastName}}
</p>
</div>
</template>
</li>
</ul>
</div>
</template>
<script>
import { viewMixin } from "../viewMixin.js";
import schemaData from '../store/schema';
import debounce from "lodash/debounce";
import api from "../store/api";
const ViewName = "AutoComplete";
var passedview;
export default {
name: "AutoComplete",
props: {
method: {
type: Function
},
title: String,
viewname:String,
autocompleteData: {
type: Array,
required: true
}
},
data() {
return {
// results: [],
dataBusinessUnit: [],
isFetching: false
// vignesh: this.objectData[this.title]
};
},
computed: {
viewData() {
return this.$store.getters.getViewData('DepartmentDetail')
},
objectData() {
return this.$store.getters.getApiData(this.viewData.api_id).data
},
sessionData() {
return this.$store.getters.getSessionData()
},
isLoading() {
return this.$store.getters.getApiData(this.viewData.api_id).isLoading
},
newRecord() {
return this.$route.params.id === null;
},
getTitle() {
return this.title
}
},
mounted() {
},
methods: {
setResult(result) {
this.updateValue(result.id,this.title);
// localStorage.setItem(this.title,result.id );
this.isFetching = false;
},
updateValue(newValue, fieldName) {
var val;
var schema = schemaData[this.viewData.schema];
if(typeof schema!=='undefined' && schema['properties'][fieldName]['type'] == 'date'){
val = this.formatDate(newValue);
} else {
val = newValue;
}
this.$store.dispatch('updateDataObjectField', {
key: this.viewData.api_id,
field: fieldName,
value: val
});
},
getAsyncDataBusinessUnit: debounce(function(name) {
console.log('getAsyncDataBusinessUnit you typed'+name.target.value);
if (!name.target.value.length) {
this.dataBusinessUnit = [];
this.isFetching = false;
return;
}
// this.isFetching = true;
api
.getSearchData(this.sessionData.key,`/businessunit/${name.target.value}`)
.then(response => {
this.dataBusinessUnit = [];
if (!response.length)
{
console.log('inside if')
this.isFetching = false;
}
else{
console.log('inside else')
response.forEach(item => {
this.dataBusinessUnit.push(item);
});
this.isFetching = true;
}
console.log('length of dataBusinessUnit is '+(this.dataBusinessUnit).length)
console.log('contents of dataBusinessUnit array '+JSON.stringify(this.dataBusinessUnit))
})
.catch(error => {
this.dataBusinessUnit = [];
throw error;
})
.finally(() => {
// this.isFetching = true;
});
}, 500),
},
components: {
}
};
</script>
The issue iam facing is whenever i start to type anything into the Custom Business Unit input field, then immediately after 1-2 seconds the value gets reset(with the value coming from the store getters ).This however does not happen if i remove the line this.dataBusinessUnit = []; in the getAsyncDataBusinessUnit function . Why is this happening? I even tried using :input instead of v-model for the input field , but i am still facing the same issue.And also second issue is when i click an existing record under DepartmentDetail page, the value that should be set for the custom business unit input field coming from the store getters (this.objectData.businessUnit) is not showing up sometimes? please help
Upvotes: 0
Views: 3736
Reputation: 8329
The basic logic of an autocomplete is not really hard:
Vue.component('Autocomplete', {
props: ['list'],
data() {
return {
input: null
}
},
template: `<div><input v-model="input" @input="handleInput"><div class="bordered" v-if="input"><ul><li v-for="(item, i) in list" :key="i" @click="setInput(item)">{{ item }}</li></ul></div></div>`,
methods: {
handleInput(e) {
this.$emit('input', e.target.value)
},
setInput(value) {
this.input = value
this.$emit('input', value)
}
}
})
new Vue({
el: "#app",
computed: {
filteredList() {
if (this.filterInput) {
return this.list.filter(e => e.toLowerCase().indexOf(this.filterInput.toLowerCase()) !== -1)
} else {
return this.list
}
}
},
data: {
filterInput: null,
list: [
"First",
"Second",
"Third",
"Fourth",
"Fifth",
"Sixth",
"Seventh"
]
},
methods: {
handleInput(e) {
this.filterInput = e
}
}
})
.bordered {
border: 1px solid black;
display: block;
}
ul li {
cursor: pointer;
}
ul li:hover {
background: rgba(0, 0, 0, 0.3)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<autocomplete :list="filteredList" @input="handleInput" />
</div>
I wouldn't handle the data in the presentational component (autocomplete
is a presentational component in the structure I created), but in its parent-container. That way the autocomplete
just gets a list as a prop and displays it; every action is $emit
ted to the parent, who does the data-handling.
It's easier to control displayed data this way - even with an async
data source.
Upvotes: 1