【问题标题】:how to create a custom autocomplete component in vue js?如何在 vue js 中创建自定义自动完成组件?
【发布时间】:2020-07-13 11:02:54
【问题描述】:

目前我正在使用 buefy 自动完成功能。但它存在一些问题。

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>  

根据用户输入获取结果的函数-

    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), 

因此,我不想使用 buefy 的 b-autocomplete,而是想创建和使用我自己的自动完成组件。 所以我继续创建了我自己的自动完成组件,并像这样从 DepartmentDetail vue 页面调用它-

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>

我面临的问题是,每当我开始在自定义业务单位输入字段中输入任何内容时,然后在 1-2 秒后立即重置该值(该值来自商店吸气剂)。但是,如果发生这种情况,则不会发生我删除行 this.dataBusinessUnit = [];在 getAsyncDataBusinessUnit 函数中。为什么会这样?我什至尝试使用 :input 而不是 v-model 作为输入字段,但我仍然面临同样的问题。第二个问题是当我单击 DepartmentDetail 页面下的现有记录时,应该为自定义业务设置的值来自商店吸气剂(this.objectData.businessUnit)的单位输入字段有时不显示?请帮忙

【问题讨论】:

    标签: html vue.js


    【解决方案1】:

    自动完成的基本逻辑并不难:

    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>

    我不会处理展示组件中的数据(autocomplete 是我创建的结构中的展示组件),而是在其父容器中处理数据。这样autocomplete 只是获取一个列表作为道具并显示它;每个动作都是$emitted 给负责数据处理的父母。

    以这种方式更容易控制显示的数据 - 即使使用 async 数据源。

    【讨论】:

    • 好的,所以即使来自 store getter (this.objectData.buisnessunit) 的值也应该在调用时作为 prop 从父组件传递给子组件(autocomplete.vue)?
    • @VigneshSwaminathan 我会这样做 - 演示组件不关心数据(例如它来自哪里),只显示它。容器组件解决了所有数据处理部分,实际上(在我的 sn-p 的简化情况下)它的唯一目的是准备(和更新)它传递给子进程的列表。如果您使用 “vue container presentational” 关键字搜索 Google,您可以阅读有关此结构的更多信息。
    • :value="this.objectData.buisnessunit"
    • 并且在子组件中,一旦收到此道具值,我应该将道具值分配给自动完成 vue 中的 v-model?
    猜你喜欢
    • 2021-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-21
    • 1970-01-01
    • 2011-05-17
    • 1970-01-01
    • 2017-09-09
    相关资源
    最近更新 更多