mirror of https://github.com/ElemeFE/element
fix autocomplete do not hide on blur bug
parent
0b524682f1
commit
0b8b011db1
|
@ -588,6 +588,7 @@ Attribute | Description | Type | Options | Default
|
|||
|custom-item | component name of your customized suggestion list item | string | — | — |
|
||||
|fetch-suggestions | a method to fetch input suggestions. When suggestions are ready, invoke `callback(data:[])` to return them to Autocomplete | Function(queryString, callback) | — | — |
|
||||
| popper-class | custom class name for autocomplete's dropdown | string | — | — |
|
||||
| trigger-on-focus | whether show suggestions when input focus | boolean | — | true |
|
||||
|
||||
### Autocomplete Events
|
||||
|
||||
|
|
|
@ -749,6 +749,7 @@ export default {
|
|||
| custom-item | 通过该参数指定自定义的输入建议列表项的组件名 | string | — | — |
|
||||
| fetch-suggestions | 返回输入建议的方法,仅当你的输入建议数据 resolve 时,通过调用 callback(data:[]) 来返回它 | Function(queryString, callback) | — | — |
|
||||
| popper-class | Autocomplete 下拉列表的类名 | string | — | — |
|
||||
| trigger-on-focus | 是否在输入框 focus 时显示建议列表 | boolean | — | true |
|
||||
|
||||
### Autocomplete Events
|
||||
| 事件名称 | 说明 | 回调参数 |
|
||||
|
|
|
@ -11,14 +11,14 @@
|
|||
<li
|
||||
v-if="!parent.customItem"
|
||||
:class="{'highlighted': parent.highlightedIndex === index}"
|
||||
@click="parent.select(index)"
|
||||
@click="select(item)"
|
||||
>
|
||||
{{item.value}}
|
||||
</li>
|
||||
<component
|
||||
v-else
|
||||
:class="{'highlighted': parent.highlightedIndex === index}"
|
||||
@click="parent.select(index)"
|
||||
@click="select(item)"
|
||||
:is="parent.customItem"
|
||||
:item="item"
|
||||
:index="index">
|
||||
|
@ -29,8 +29,9 @@
|
|||
</template>
|
||||
<script>
|
||||
import Popper from 'element-ui/src/utils/vue-popper';
|
||||
import Emitter from 'element-ui/src/mixins/emitter';
|
||||
export default {
|
||||
mixins: [Popper],
|
||||
mixins: [Popper, Emitter],
|
||||
|
||||
componentName: 'ElAutocompleteSuggestions',
|
||||
|
||||
|
@ -53,6 +54,12 @@
|
|||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
select(item) {
|
||||
this.dispatch('ElAutocomplete', 'item-click', item);
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.popperElm = this.$el;
|
||||
this.referenceElm = this.$parent.$refs.input.$refs.input;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="el-autocomplete" v-clickoutside="handleBlur">
|
||||
<div class="el-autocomplete" v-clickoutside="handleClickoutside">
|
||||
<el-input
|
||||
ref="input"
|
||||
:value="value"
|
||||
|
@ -9,9 +9,10 @@
|
|||
:size="size"
|
||||
@change="handleChange"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur"
|
||||
@keydown.up.native="highlight(highlightedIndex - 1)"
|
||||
@keydown.down.native="highlight(highlightedIndex + 1)"
|
||||
@keydown.enter.stop.native="select(highlightedIndex)"
|
||||
@keydown.enter.stop.native="handleKeyEnter"
|
||||
>
|
||||
<template slot="prepend" v-if="$slots.prepend">
|
||||
<slot name="prepend"></slot>
|
||||
|
@ -39,6 +40,8 @@
|
|||
|
||||
mixins: [Emitter],
|
||||
|
||||
componentName: 'ElAutocomplete',
|
||||
|
||||
components: {
|
||||
ElInput,
|
||||
ElAutocompleteSuggestions
|
||||
|
@ -53,6 +56,7 @@
|
|||
name: String,
|
||||
size: String,
|
||||
value: String,
|
||||
autofocus: Boolean,
|
||||
fetchSuggestions: Function,
|
||||
triggerOnFocus: {
|
||||
type: Boolean,
|
||||
|
@ -62,53 +66,65 @@
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
isFocus: false,
|
||||
suggestions: [],
|
||||
suggestionVisible: false,
|
||||
loading: false,
|
||||
highlightedIndex: -1
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
suggestionVisible() {
|
||||
const suggestions = this.suggestions;
|
||||
let isValidData = Array.isArray(suggestions) && suggestions.length > 0;
|
||||
return (isValidData || this.loading) && this.isFocus;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
suggestionVisible(val) {
|
||||
this.broadcast('ElAutocompleteSuggestions', 'visible', [val, this.$refs.input.$refs.input.offsetWidth]);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getData(queryString) {
|
||||
this.loading = true;
|
||||
this.fetchSuggestions(queryString, (suggestions) => {
|
||||
this.loading = false;
|
||||
if (Array.isArray(suggestions)) {
|
||||
this.suggestions = suggestions;
|
||||
} else {
|
||||
console.error('autocomplete suggestions must be an array');
|
||||
}
|
||||
});
|
||||
},
|
||||
handleChange(value) {
|
||||
this.$emit('input', value);
|
||||
this.showSuggestions(value);
|
||||
this.getData(value);
|
||||
},
|
||||
handleFocus() {
|
||||
this.isFocus = true;
|
||||
if (this.triggerOnFocus) {
|
||||
this.showSuggestions(this.value);
|
||||
this.getData(this.value);
|
||||
}
|
||||
},
|
||||
handleBlur() {
|
||||
this.hideSuggestions();
|
||||
// 因为 blur 事件处理优先于 select 事件执行
|
||||
setTimeout(_ => {
|
||||
this.isFocus = false;
|
||||
}, 100);
|
||||
},
|
||||
select(index) {
|
||||
if (this.suggestions && this.suggestions[index]) {
|
||||
this.$emit('input', this.suggestions[index].value);
|
||||
this.$emit('select', this.suggestions[index]);
|
||||
this.$nextTick(() => {
|
||||
this.hideSuggestions();
|
||||
});
|
||||
handleKeyEnter() {
|
||||
if (this.suggestionVisible) {
|
||||
this.select(this.suggestions[this.highlightedIndex]);
|
||||
}
|
||||
},
|
||||
hideSuggestions() {
|
||||
this.suggestionVisible = false;
|
||||
this.loading = false;
|
||||
handleClickoutside() {
|
||||
this.isFocus = false;
|
||||
},
|
||||
showSuggestions(value) {
|
||||
this.suggestionVisible = true;
|
||||
this.loading = true;
|
||||
this.fetchSuggestions(value, (suggestions) => {
|
||||
this.loading = false;
|
||||
if (Array.isArray(suggestions) && suggestions.length > 0) {
|
||||
this.suggestions = suggestions;
|
||||
} else {
|
||||
this.hideSuggestions();
|
||||
}
|
||||
select(item) {
|
||||
this.$emit('input', item.value);
|
||||
this.$emit('select', item);
|
||||
this.$nextTick(_ => {
|
||||
this.suggestions = [];
|
||||
});
|
||||
},
|
||||
highlight(index) {
|
||||
|
@ -134,6 +150,11 @@
|
|||
this.highlightedIndex = index;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$on('item-click', item => {
|
||||
this.select(item);
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$refs.suggestions.$destroy();
|
||||
}
|
||||
|
|
|
@ -8,12 +8,15 @@ describe('Autocomplete', () => {
|
|||
it('create', done => {
|
||||
vm = createVue({
|
||||
template: `
|
||||
<el-autocomplete
|
||||
ref="autocomplete"
|
||||
v-model="state"
|
||||
:fetch-suggestions="querySearch"
|
||||
placeholder="请输入内容autocomplete1"
|
||||
></el-autocomplete>
|
||||
<div>
|
||||
<button class="btn">a</button>
|
||||
<el-autocomplete
|
||||
ref="autocomplete"
|
||||
v-model="state"
|
||||
:fetch-suggestions="querySearch"
|
||||
placeholder="请输入内容autocomplete1"
|
||||
></el-autocomplete>
|
||||
</div>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
|
@ -52,12 +55,13 @@ describe('Autocomplete', () => {
|
|||
expect(inputElm.getAttribute('placeholder')).to.be.equal('请输入内容autocomplete1');
|
||||
|
||||
setTimeout(_ => {
|
||||
let suggestionsList = vm.$refs.autocomplete.$refs.suggestions.$el;
|
||||
expect(suggestionsList.style.display).to.not.equal('none');
|
||||
expect(suggestionsList.children.length).to.be.equal(4);
|
||||
const suggestions = vm.$refs.autocomplete.$refs.suggestions.$el;
|
||||
expect(suggestions.style.display).to.not.equal('none');
|
||||
expect(suggestions.children.length).to.be.equal(4);
|
||||
|
||||
document.body.click();
|
||||
setTimeout(_ => {
|
||||
expect(document.querySelector('.el-autocomplete__suggestions').style.display).to.be.equal('none');
|
||||
expect(suggestions.style.display).to.be.equal('none');
|
||||
done();
|
||||
}, 500);
|
||||
}, 500);
|
||||
|
@ -276,4 +280,55 @@ describe('Autocomplete', () => {
|
|||
});
|
||||
}, 500);
|
||||
});
|
||||
it('triggerOnFocus', done => {
|
||||
vm = createVue({
|
||||
template: `
|
||||
<el-autocomplete
|
||||
ref="autocomplete"
|
||||
v-model="state"
|
||||
:fetch-suggestions="querySearch"
|
||||
:trigger-on-focus="false"
|
||||
placeholder="请输入内容autocomplete1"
|
||||
></el-autocomplete>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
restaurants: [],
|
||||
state: ''
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
querySearch(queryString, cb) {
|
||||
var restaurants = this.restaurants;
|
||||
var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants;
|
||||
cb(results);
|
||||
},
|
||||
createFilter(queryString) {
|
||||
return (restaurant) => {
|
||||
return (restaurant.value.indexOf(queryString.toLowerCase()) === 0);
|
||||
};
|
||||
},
|
||||
loadAll() {
|
||||
return [
|
||||
{ 'value': '三全鲜食(北新泾店)', 'address': '长宁区新渔路144号' },
|
||||
{ 'value': 'Hot honey 首尔炸鸡(仙霞路)', 'address': '上海市长宁区淞虹路661号' },
|
||||
{ 'value': '新旺角茶餐厅', 'address': '上海市普陀区真北路988号创邑金沙谷6号楼113' },
|
||||
{ 'value': '泷千家(天山西路店)', 'address': '天山西路438号' }
|
||||
];
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.restaurants = this.loadAll();
|
||||
}
|
||||
}, true);
|
||||
let elm = vm.$el;
|
||||
let inputElm = elm.querySelector('input');
|
||||
inputElm.focus();
|
||||
|
||||
setTimeout(_ => {
|
||||
let suggestionsList = vm.$refs.autocomplete.$refs.suggestions.$el;
|
||||
expect(suggestionsList.style.display).to.be.equal('none');
|
||||
done();
|
||||
}, 500);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue