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 | — | — |
|
|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) | — | — |
|
|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 | — | — |
|
| popper-class | custom class name for autocomplete's dropdown | string | — | — |
|
||||||
|
| trigger-on-focus | whether show suggestions when input focus | boolean | — | true |
|
||||||
|
|
||||||
### Autocomplete Events
|
### Autocomplete Events
|
||||||
|
|
||||||
|
|
|
@ -749,6 +749,7 @@ export default {
|
||||||
| custom-item | 通过该参数指定自定义的输入建议列表项的组件名 | string | — | — |
|
| custom-item | 通过该参数指定自定义的输入建议列表项的组件名 | string | — | — |
|
||||||
| fetch-suggestions | 返回输入建议的方法,仅当你的输入建议数据 resolve 时,通过调用 callback(data:[]) 来返回它 | Function(queryString, callback) | — | — |
|
| fetch-suggestions | 返回输入建议的方法,仅当你的输入建议数据 resolve 时,通过调用 callback(data:[]) 来返回它 | Function(queryString, callback) | — | — |
|
||||||
| popper-class | Autocomplete 下拉列表的类名 | string | — | — |
|
| popper-class | Autocomplete 下拉列表的类名 | string | — | — |
|
||||||
|
| trigger-on-focus | 是否在输入框 focus 时显示建议列表 | boolean | — | true |
|
||||||
|
|
||||||
### Autocomplete Events
|
### Autocomplete Events
|
||||||
| 事件名称 | 说明 | 回调参数 |
|
| 事件名称 | 说明 | 回调参数 |
|
||||||
|
|
|
@ -11,14 +11,14 @@
|
||||||
<li
|
<li
|
||||||
v-if="!parent.customItem"
|
v-if="!parent.customItem"
|
||||||
:class="{'highlighted': parent.highlightedIndex === index}"
|
:class="{'highlighted': parent.highlightedIndex === index}"
|
||||||
@click="parent.select(index)"
|
@click="select(item)"
|
||||||
>
|
>
|
||||||
{{item.value}}
|
{{item.value}}
|
||||||
</li>
|
</li>
|
||||||
<component
|
<component
|
||||||
v-else
|
v-else
|
||||||
:class="{'highlighted': parent.highlightedIndex === index}"
|
:class="{'highlighted': parent.highlightedIndex === index}"
|
||||||
@click="parent.select(index)"
|
@click="select(item)"
|
||||||
:is="parent.customItem"
|
:is="parent.customItem"
|
||||||
:item="item"
|
:item="item"
|
||||||
:index="index">
|
:index="index">
|
||||||
|
@ -29,8 +29,9 @@
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import Popper from 'element-ui/src/utils/vue-popper';
|
import Popper from 'element-ui/src/utils/vue-popper';
|
||||||
|
import Emitter from 'element-ui/src/mixins/emitter';
|
||||||
export default {
|
export default {
|
||||||
mixins: [Popper],
|
mixins: [Popper, Emitter],
|
||||||
|
|
||||||
componentName: 'ElAutocompleteSuggestions',
|
componentName: 'ElAutocompleteSuggestions',
|
||||||
|
|
||||||
|
@ -53,6 +54,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
select(item) {
|
||||||
|
this.dispatch('ElAutocomplete', 'item-click', item);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.popperElm = this.$el;
|
this.popperElm = this.$el;
|
||||||
this.referenceElm = this.$parent.$refs.input.$refs.input;
|
this.referenceElm = this.$parent.$refs.input.$refs.input;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="el-autocomplete" v-clickoutside="handleBlur">
|
<div class="el-autocomplete" v-clickoutside="handleClickoutside">
|
||||||
<el-input
|
<el-input
|
||||||
ref="input"
|
ref="input"
|
||||||
:value="value"
|
:value="value"
|
||||||
|
@ -9,9 +9,10 @@
|
||||||
:size="size"
|
:size="size"
|
||||||
@change="handleChange"
|
@change="handleChange"
|
||||||
@focus="handleFocus"
|
@focus="handleFocus"
|
||||||
|
@blur="handleBlur"
|
||||||
@keydown.up.native="highlight(highlightedIndex - 1)"
|
@keydown.up.native="highlight(highlightedIndex - 1)"
|
||||||
@keydown.down.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">
|
<template slot="prepend" v-if="$slots.prepend">
|
||||||
<slot name="prepend"></slot>
|
<slot name="prepend"></slot>
|
||||||
|
@ -39,6 +40,8 @@
|
||||||
|
|
||||||
mixins: [Emitter],
|
mixins: [Emitter],
|
||||||
|
|
||||||
|
componentName: 'ElAutocomplete',
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ElInput,
|
ElInput,
|
||||||
ElAutocompleteSuggestions
|
ElAutocompleteSuggestions
|
||||||
|
@ -53,6 +56,7 @@
|
||||||
name: String,
|
name: String,
|
||||||
size: String,
|
size: String,
|
||||||
value: String,
|
value: String,
|
||||||
|
autofocus: Boolean,
|
||||||
fetchSuggestions: Function,
|
fetchSuggestions: Function,
|
||||||
triggerOnFocus: {
|
triggerOnFocus: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
@ -62,53 +66,65 @@
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
isFocus: false,
|
||||||
suggestions: [],
|
suggestions: [],
|
||||||
suggestionVisible: false,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
highlightedIndex: -1
|
highlightedIndex: -1
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
suggestionVisible() {
|
||||||
|
const suggestions = this.suggestions;
|
||||||
|
let isValidData = Array.isArray(suggestions) && suggestions.length > 0;
|
||||||
|
return (isValidData || this.loading) && this.isFocus;
|
||||||
|
}
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
suggestionVisible(val) {
|
suggestionVisible(val) {
|
||||||
this.broadcast('ElAutocompleteSuggestions', 'visible', [val, this.$refs.input.$refs.input.offsetWidth]);
|
this.broadcast('ElAutocompleteSuggestions', 'visible', [val, this.$refs.input.$refs.input.offsetWidth]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
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) {
|
handleChange(value) {
|
||||||
this.$emit('input', value);
|
this.$emit('input', value);
|
||||||
this.showSuggestions(value);
|
this.getData(value);
|
||||||
},
|
},
|
||||||
handleFocus() {
|
handleFocus() {
|
||||||
|
this.isFocus = true;
|
||||||
if (this.triggerOnFocus) {
|
if (this.triggerOnFocus) {
|
||||||
this.showSuggestions(this.value);
|
this.getData(this.value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleBlur() {
|
handleBlur() {
|
||||||
this.hideSuggestions();
|
// 因为 blur 事件处理优先于 select 事件执行
|
||||||
|
setTimeout(_ => {
|
||||||
|
this.isFocus = false;
|
||||||
|
}, 100);
|
||||||
},
|
},
|
||||||
select(index) {
|
handleKeyEnter() {
|
||||||
if (this.suggestions && this.suggestions[index]) {
|
if (this.suggestionVisible) {
|
||||||
this.$emit('input', this.suggestions[index].value);
|
this.select(this.suggestions[this.highlightedIndex]);
|
||||||
this.$emit('select', this.suggestions[index]);
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.hideSuggestions();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hideSuggestions() {
|
handleClickoutside() {
|
||||||
this.suggestionVisible = false;
|
this.isFocus = false;
|
||||||
this.loading = false;
|
|
||||||
},
|
},
|
||||||
showSuggestions(value) {
|
select(item) {
|
||||||
this.suggestionVisible = true;
|
this.$emit('input', item.value);
|
||||||
this.loading = true;
|
this.$emit('select', item);
|
||||||
this.fetchSuggestions(value, (suggestions) => {
|
this.$nextTick(_ => {
|
||||||
this.loading = false;
|
this.suggestions = [];
|
||||||
if (Array.isArray(suggestions) && suggestions.length > 0) {
|
|
||||||
this.suggestions = suggestions;
|
|
||||||
} else {
|
|
||||||
this.hideSuggestions();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
highlight(index) {
|
highlight(index) {
|
||||||
|
@ -134,6 +150,11 @@
|
||||||
this.highlightedIndex = index;
|
this.highlightedIndex = index;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$on('item-click', item => {
|
||||||
|
this.select(item);
|
||||||
|
});
|
||||||
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.$refs.suggestions.$destroy();
|
this.$refs.suggestions.$destroy();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,15 @@ describe('Autocomplete', () => {
|
||||||
it('create', done => {
|
it('create', done => {
|
||||||
vm = createVue({
|
vm = createVue({
|
||||||
template: `
|
template: `
|
||||||
<el-autocomplete
|
<div>
|
||||||
ref="autocomplete"
|
<button class="btn">a</button>
|
||||||
v-model="state"
|
<el-autocomplete
|
||||||
:fetch-suggestions="querySearch"
|
ref="autocomplete"
|
||||||
placeholder="请输入内容autocomplete1"
|
v-model="state"
|
||||||
></el-autocomplete>
|
:fetch-suggestions="querySearch"
|
||||||
|
placeholder="请输入内容autocomplete1"
|
||||||
|
></el-autocomplete>
|
||||||
|
</div>
|
||||||
`,
|
`,
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -52,12 +55,13 @@ describe('Autocomplete', () => {
|
||||||
expect(inputElm.getAttribute('placeholder')).to.be.equal('请输入内容autocomplete1');
|
expect(inputElm.getAttribute('placeholder')).to.be.equal('请输入内容autocomplete1');
|
||||||
|
|
||||||
setTimeout(_ => {
|
setTimeout(_ => {
|
||||||
let suggestionsList = vm.$refs.autocomplete.$refs.suggestions.$el;
|
const suggestions = vm.$refs.autocomplete.$refs.suggestions.$el;
|
||||||
expect(suggestionsList.style.display).to.not.equal('none');
|
expect(suggestions.style.display).to.not.equal('none');
|
||||||
expect(suggestionsList.children.length).to.be.equal(4);
|
expect(suggestions.children.length).to.be.equal(4);
|
||||||
|
|
||||||
document.body.click();
|
document.body.click();
|
||||||
setTimeout(_ => {
|
setTimeout(_ => {
|
||||||
expect(document.querySelector('.el-autocomplete__suggestions').style.display).to.be.equal('none');
|
expect(suggestions.style.display).to.be.equal('none');
|
||||||
done();
|
done();
|
||||||
}, 500);
|
}, 500);
|
||||||
}, 500);
|
}, 500);
|
||||||
|
@ -276,4 +280,55 @@ describe('Autocomplete', () => {
|
||||||
});
|
});
|
||||||
}, 500);
|
}, 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