【问题标题】:Is v-autocomplete compatible with Google Places Autocomplete APIv-autocomplete 是否与 Google Places Autocomplete API 兼容
【发布时间】:2018-12-30 08:30:23
【问题描述】:

我尝试使用Google Places Autocomplete API 填充Vuetify Autocomplete Component,如下所示:

<template>
  <v-autocomplete
    ref="autocomplete" 
    label="Location"
  >
  </v-autocomplete>
</template>

<script>
export default {
  mounted() {
    var autocomplete = new google.maps.places.Autocomplete(
      /** @type {!HTMLInputElement} */(this.$refs.autocomplete),
      {types: ['geocode']})
  }
}
</script>

但是,在浏览器的开发者控制台中,却抛出了一个错误:

InvalidValueError: 不是 HTMLInputElement 的实例

我的猜测是 v-autocomplete 不是 HTMLInputElement 类型。

(不仅v-autocomplete 是这种情况:用v-input 替换它也会导致同样的错误。)

有没有办法让v-autocomplete 填充 Google Places Autocomplete API,比如手动将其设置为 HTMLInputElement 的实例?

【问题讨论】:

    标签: javascript vue.js autocomplete google-places-api vuetify.js


    【解决方案1】:

    当仅使用google.maps.places.Autocomplete 时,似乎没有直接的方法来保持v-autocomplete 的材料外观。为了实现这一点,我包装了 API 的 getPlacePredictions() 方法 - 而不是组件 - 称为 Autocomplete Service

    PlacesUtils.js

    /* global google */
    
    const GetSuggestions = async searchText => {
      let result
    
      try {
        const rawResult = await searchLocation(searchText)
        result = rawResult.map((res) => {
          return {
            id: res.place_id,
            value: res.description
          }
        })
      } catch (err) {
        console.log('An error occurred', err)
        result = null
      }
      return result
    }
    
    // Auxiliary functions
    // wrap google api's callback to an async function
    const searchLocation = async val => {
      let promise = await new Promise((resolve, reject) => {
        var displaySuggestions = (predictions, status) => {
          if (status !== google.maps.places.PlacesServiceStatus.OK) {
            reject(status)
          }
          resolve(predictions)
        }
    
        var service = new google.maps.places.AutocompleteService()
        service.getPlacePredictions({
          input: val,
          types: ['geocode']
        },
        displaySuggestions)
      }).catch(function (err) { throw err })
    
      return promise
    }
    
    export { GetSuggestions }
    

    然后,为v-autocomplete 的模型添加watch,我根据用户所做的更改调用此方法,如下所示:

    Place.vue

    <template>
      <v-layout row justify-center>
        <!-- ... -->
          <v-autocomplete
            label="Location"
            v-model="autocompleteLocationModel"
            :items="locationFoundItems"
            :search-input.sync="locationSearchText"
            item-text="value"
            item-value="id"
            hide-no-data
            return-object
          >
        </v-autocomplete>
        <!-- ... -->
      </v-layout>
    </template>
    
    <script>
    /* eslint handle-callback-err: "warn" */
    import { GetSuggestions } from '@/utils/PlaceUtils'
    
    export default {
      data () {
        return {
          autocompleteLocationModel: null,
          locationSearchText: null,
          locationEntries: []
        }
      },
      computed: {
        locationFoundItems () {
          return this.locationEntries
        }
      },
      watch: {
        locationSearchText (newVal) {
          var _vue = this
    
          // If less than 3 chars typed, do not search
          if (!newVal || newVal.length <= 3) return
    
          // Call the method from the previous section here
          GetSuggestions(newVal)
            .then(function (res) {
              _vue.locationEntries = res
            })
            .catch(function (err) {
              // error handling goes here
            })
        }
      }
      // ...
    }
    </script>
    

    【讨论】:

      【解决方案2】:

      我现在也在处理这个问题,让它部分工作,当我完全清除它时会更新,但现在你的代码有问题。

      当您在模板中应用ref="autocomplete" 时,您将ref 应用到组件而不是输入。为了让它工作,我设置了一个id="autocomplete",它直接应用于输入,在我的挂载函数中创建了一个变量来引用输入id,然后我将它传递给自动完成函数。我在下面更新了您的代码以反映这一点。

      <template>
        <v-autocomplete
          id="autocomplete" 
          label="Location"
        >
        </v-autocomplete>
      </template>
      
      <script>
      export default {
        mounted() {
          var autocompleteInput = document.querySelector('#autocomplete');
          var autocomplete = new google.maps.places.Autocomplete(
            /** @type {!HTMLInputElement} */(autocompleteInput),
            {types: ['geocode']})
        }
      }
      </script>
      

      您可以将相同的原则应用于v-text field,但谷歌自动完成结果将出现在输入下方的自己的容器中,而不是像v-autocomplete 这样的选择下拉容器中。

      【讨论】:

      • 很好的解决方法,但遗憾的是它扭曲了v-autocomplete 的外观。
      【解决方案3】:

      @vahdet 我要感谢您的代码,我在其中用作一个完整的组件,在“地点”上发出选定地点的详细信息谢谢您的帮助!

      <template>
        <v-layout row justify-center>
          <!-- ... -->
          <v-autocomplete
            label="Location"
            id="decoy"
            v-model="autocompleteLocationModel"
            :items="locationFoundItems"
            :search-input.sync="locationSearchText"
            item-text="value"
            item-value="id"
            hide-no-data
            return-object
          >
          </v-autocomplete>
          <!-- ... -->
        </v-layout>
      </template>
      
      <script>
      /* eslint handle-callback-err: "warn" */
      import { GetSuggestions } from "../../../PlacesUtils";
      
      export default {
        data() {
          return {
            autocompleteLocationModel: null,
            locationSearchText: null,
            locationEntries: [],
          };
        },
        computed: {
          locationFoundItems() {
            return this.locationEntries;
          },
        },
        watch: {
          autocompleteLocationModel(newVal) {
            console.log(newVal.id);
            let resplace = new google.maps.places.PlacesService(
              document.getElementById("decoy")
            );
      
            resplace.getDetails(
              {
                placeId: newVal.id
              },
              (x) => {
                this.$emit("place", x);
              }
            );
          },
      
          locationSearchText(newVal) {
            var _vue = this;
      
            // If less than 3 chars typed, do not search
            if (!newVal || newVal.length <= 3) return;
      
            // Call the method from the previous section here
            GetSuggestions(newVal)
              .then(function(res) {
                _vue.locationEntries = res;
              })
              .catch(function(err) {
                // error handling goes here
                console.log(err);
              });
          },
        },
      };
      </script>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-11-09
        • 2017-01-23
        • 2022-06-19
        • 2012-08-20
        • 2017-06-12
        • 1970-01-01
        • 1970-01-01
        • 2016-08-15
        相关资源
        最近更新 更多