Does v-autocomplete support integration with the Google Places Autocomplete API?

Trying to integrate the Google Places Autocomplete API with the Vuetify Autocomplete Component can be achieved using the following code:

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

After implementing this, an error is displayed in the developer console:

InvalidValueError: not an instance of HTMLInputElement

It seems that the element v-autocomplete is not considered as an HTMLInputElement.

(Interestingly, changing v-autocomplete to v-input still results in the same error.)

Is there a solution to convert v-autocomplete into an HTMLInputElement so that it can be successfully integrated with the Google Places Autocomplete API?

Answer №1

There seems to be no direct way to maintain the visual appearance of v-autocomplete while using only google.maps.places.Autocomplete. In order to achieve this, I have wrapped the getPlacePredictions() method of the API - which is not a component - and instead called it from the 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
// wrapping google api's callback in 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 }

Next, by adding a watch for the model of v-autocomplete, I invoke this method whenever the user makes changes:

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

      // Do not search if less than 3 characters typed
      if (!newVal || newVal.length <= 3) return

      // Call the method mentioned earlier here
      GetSuggestions(newVal)
        .then(function (res) {
          _vue.locationEntries = res
        })
        .catch(function (err) {
          // error handling logic goes here
        })
    }
  }
  // ...
}
</script>

Answer №2

Currently, I am experimenting with this feature and have made some progress. I will provide a comprehensive update once I finalize the implementation. The issue in your code lies here:

When you use ref="autocomplete" in your template, it is applied to the component instead of the input element. To rectify this, I assigned an directly to the input field. Next, I created a variable within my mounted function to store the ID of the input, which I then passed into the autocomplete function. Below is the revised version of your code reflecting these changes:

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

This same approach can be applied to a v-text field, but note that the Google Autocomplete results will display in a separate container below the input field rather than within the select dropdown like in v-autocomplete.

Answer №3

Appreciation to @vahdet for the helpful code that allowed me to create a component which presents detailed information about selected places on the event "place". Your assistance is greatly appreciated!

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Problem with MongoDB - increasing number of connections

I have encountered an issue with my current approach to connecting to MongoDB. The method I am using is outlined below: import { Db, MongoClient } from "mongodb"; let cachedConnection: { client: MongoClient; db: Db } | null = null; export asyn ...

Launching the development server on a project created using Vue CLI 3

After successfully installing Vue CLI 3 globally on my Windows system using the command npm i -g @vue/cli, I proceeded to generate a project with the command vue create vue-project. I made sure to select all the necessary plugins as prompted during the se ...

What is the best way to split strings in an input field?

My task is to create a form with an input field and a dropdown list containing options for Checkbox and Radio. The user should enter text in the input field and select a type from the dropdown. For example: Input field: One, Two, Three Radio The expecte ...

Error in validating Javascript, Ajax, and PHP form entries

In order to ensure that only valid authors are added to my database on the bookshop website, I have implemented a form validation system. While the author check function works perfectly and alerts users when an author is found, there seems to be an issue w ...

What is the reason for needing to refresh when submitting form data in a Node application with an HTTP POST

Code Snippet - Angular .state('studentInfo.newStudent', { url : '/new/student', templateUrl: 'students/new-student.html', controller : function($state, $http){ this.saveStudent = func ...

Can you explain the distinction between App: React.FunctionComponent and App = (): React.FunctionComponent()?

Currently exploring the depths of typescript. Can someone clarify the distinction between these two code snippets: const App: React.FunctionComponent<CustomProps> = (props: CustomProps) => { return <div>Hello World!</div>; }; and: ...

Guide to automatically choose the first item and emphasize the chosen selection in AngularJs

I am currently developing an app that showcases a list of items on the left side. By default, the first item is highlighted and its details are displayed on the right side. When a user clicks on any item in the list, that item should be highlighted and its ...

The AJAX request encountered an unexpected failure that cannot be identified (Using jQuery)

Are you facing issues with a service that returns JSON data? Check out this URL: If you're attempting a simple AJAX request, here's some sample code to get you started: $.ajax({ type: "get", url: "http://api.drag2droid.shamanland.com/ca ...

Creating a dialog form using Vuetify in a VueJS application

Using the VueJS Vuetify framework, I am faced with a task of opening a dialog form - which is imported as a component template - from another template. When the ChangeMealDialog button in Meals.vue is clicked, the Modal should pop up. Below is the configur ...

When utilizing jQuery lightbox to pull data from a database using PHP/Ajax, it may require a double click the

Encountering a strange issue where I must click on specific buttons with unique IDs. These IDs are then sent through Ajax to a PHP script, which searches for corresponding entries in the database. The retrieved data is then displayed in a jQuery lightbox. ...

Leveraging Javascript to generate universal HTML content for various Javascript files

Present Situation: I have a timesheet feature that enables users to input their leave, TOIL, and sick days along with the respective hours. Additionally, there is a table that dynamically adds a new row every time the plus button is clicked using the foll ...

The Star Rating System fails to update accurately after hiding the Radio Buttons

I followed a tutorial to set up a Star Rating System Everything was working fine on the SHOW PAGE and the INDEX PAGE until I decided to hide the Radio Buttons and use the corresponding labels to submit the value. I encountered an issue where the JavaScrip ...

Saving the output of a function in Javascript/NodeJS to a variable

I've been attempting to save the output of a function into a variable, but it's not working as expected. I've exhausted all my ideas and possibilities. Perhaps I'm just making a silly mistake :D I'm working with NodeJS, using expre ...

Guide for implementing props in a text area component using React and TypeScript

My react component has a sleek design: import { TextareaHTMLAttributes} from 'react' import styled from 'styled-components' const TextAreaElement = styled.textarea` border-radius: 40px; border: none; background: white; ` const T ...

Error: Uncaught ReferenceError: d3 is undefined. The script is not properly referenced

Entering the world of web development, I usually find solutions on Stack Overflow. However, this time I'm facing a challenge. I am using Firefox 32 with Firebug as my debugger. The webpage I have locally runs with the following HTML Code <!DOCTYP ...

Potential Javascript timing problem encountered during webpage initialization – involving the implementation of a dynamic bootstrap progress

I have limited knowledge of javascript, but I stumbled upon this amazing fiddle that I would like to incorporate into my project: http://jsfiddle.net/5w5ku/1/ The issue I am facing is that I want it to persist for a duration of ten minutes. Despite atte ...

Creating a transparent background in a three.js canvas: a step-by-step guide

I came across a wave particle animation on Codepen (https://codepen.io/kevinsturf/pen/ExLdPZ) that I want to use, but it has a white background. However, when I try to set the body background to red, it doesn't show up once the canvas is rendered. I ...

To dismiss a popup on a map, simply click on any area outside the map

Whenever I interact with a map similar to Google Maps by clicking on various points, a dynamically generated popup appears. However, I am facing an issue where I want to close this popup when clicking outside the map area. Currently, the code I have writte ...

Error 422: Issues with posting Laravel ajax form on Microsoft Edge browser

I am facing an issue with a form that I have implemented in my Laravel controller. The form works perfectly fine on Chrome, Safari, and Firefox, but it gives a 422 (unprocessable entity) error on Edge browser. Could someone help me figure out what might b ...

What is the best way to display a PDF in a web browser using a JavaScript byte array?

I have a controller that sends the response Entity as a byte array in PDF form to an ajax call. However, I am struggling to display it in the browser despite trying various suggestions from old Stack Overflow questions. Here is the response from the Sprin ...