v-autocomplete是否与Google Places Autocomplete API兼容

时间:2018-07-22 21:36:46

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

我尝试利用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的实例?

3 个答案:

答案 0 :(得分: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,但是google自动完成结果将显示在输入下方的自己的容器中,而不是出现在v-autocomplete之类的选择下拉容器中。

答案 1 :(得分:0)

仅使用v-autocomplete时,似乎并没有直接的方法来保持google.maps.places.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 }

然后,为watch的模型添加一个v-autocomplete,我在用户做出如下更改时调用此方法:

  

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 :(得分:0)

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