Bläddra i källkod

fix autocomplete do not hide on blur bug

baiyaaaaa 8 år sedan
förälder
incheckning
0b8b011db1

+ 1 - 0
examples/docs/en-US/input.md

@@ -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
 

+ 1 - 0
examples/docs/zh-CN/input.md

@@ -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
 | 事件名称 | 说明 | 回调参数 |

+ 10 - 3
packages/autocomplete/src/autocomplete-suggestions.vue

@@ -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;

+ 47 - 26
packages/autocomplete/src/autocomplete.vue

@@ -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();
     }

+ 65 - 10
test/unit/specs/autocomplete.spec.js

@@ -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);
+  });
 });