首页 > 解决方案 > VueJS:如何使用具有多个 refs 的 Google Places API

问题描述

我正在使用 Google Places API 进行工作,一切正常。但是,我有一个独特的场景,我需要有多个送货地址,因此我需要让 Google Place API 以这种方式工作。

这是我当前的代码:

<template>
  <div>
    <div v-for="(search, index) in searchInput" :key="index">
      <input type="text" ref="search" v-model="search.city">
    </div>
    <button type="button" @click="addMore">Add more</button>
  </div>
</template>

<script>
export default {
  name: "App",
  data: () => ({
    location: null,
    searchInput: [
      {
        city: ""
      }
    ]
  }),
  mounted() {
    window.checkAndAttachMapScript(this.initLocationSearch);
  },
  methods: {
    initLocationSearch() {
      let autocomplete = new window.google.maps.places.Autocomplete(
        this.$refs.search
      );
      autocomplete.addListener("place_changed", function() {
        let place = autocomplete.getPlace();
        if (place && place.address_components) {
          console.log(place.address_components);
        }
      });
    },
    addMore() {
      this.searchInput.push({ city: "" });
    }
  }
};
</script>

我从 API 收到此错误:InvalidValueError: not an instance of HTMLInputElementb.ownerDocument is undefined

如何refs为我添加的单个项目设置唯一性,然后将其传递给initLocationSearch方法?请,任何帮助将不胜感激。

她是一个Codesandbox演示

标签: javascriptgoogle-mapsvue.jsgoogle-places-apigoogleplacesautocomplete

解决方案


鉴于您为完整场景提供的详细信息,我将这样做。

我会使用一个简单的计数器来跟踪输入的数量并将其初始值设置为1并使用它来显示输入。

<div v-for="(n, index) in this.counter" :key="index">
  <input type="text" ref="search">
</div>

然后根据计数器将自动完成小部件绑定到最后添加的输入。

autocomplete = new window.google.maps.places.Autocomplete(
  this.$refs.search[this.counter - 1]
);

并使用您的addMore()方法来增加计数器。

addMore() {
  this.counter++;
}

用于updated()每当向 DOM 添加新输入以触发该initLocationSearch()方法时。

updated() {
  this.initLocationSearch();
},

place_changed事件发生时,根据各种输入值更新选定城市的列表。

this.selectedCities = this.$refs["search"].map(item => {
  return { city: item.value };
});

这样,无论输入发生什么变化,您都始终拥有最新的城市列表。当然,您需要处理清空输入以及可能的其他边缘情况。

完整代码:

<template>
  <div>
    <div v-for="(n, index) in this.counter" :key="index">
      <input type="text" ref="search">
    </div>
    <button type="button" @click="addMore">Add more</button>
    <hr>Selected cities:
    <ul>
      <li v-for="(item, index) in this.selectedCities" :key="index">{{ item.city }}</li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "App",
  data: () => ({
    selectedCities: [],
    counter: 1
  }),
  mounted() {
    window.checkAndAttachMapScript(this.initLocationSearch);
  },
  updated() {
    this.initLocationSearch();
  },
  methods: {
    setSelectedCities() {
      this.selectedCities = this.$refs["search"].map(item => {
        return { city: item.value };
      });
    },
    initLocationSearch() {
      let self = this,
        autocomplete = new window.google.maps.places.Autocomplete(
          this.$refs.search[this.counter - 1],
          {
            fields: ["address_components"] // Reduce API costs by specifying fields
          }
        );

      autocomplete.addListener("place_changed", function() {
        let place = autocomplete.getPlace();
        if (place && place.address_components) {
          console.log(place);
          self.setSelectedCities();
        }
      });
    },
    addMore() {
      this.counter++;
    }
  }
};
</script>

CodeSandbox 演示


推荐阅读